Spring Boot #Before inserted data not available - java

I'm writing a simple authentication test with JUnit4 and TestContainers. Both my #Before and #After methods are #Transactional, but when I query for the data in UserDetailsServiceImpl it is not present and I can not figure out why. There is nothing async. Any ideas.
#Slf4j
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
#TestPropertySource(locations = "classpath:application-test.properties")
public abstract class DBEnabledTest {
#Autowired
protected EntityManager em;
#ClassRule
public static PostgreSQLContainer<?> sqlContainer = new PostgreSQLContainer<>("postgres:11")
.withDatabaseName("test_scenarios")
.withUsername("postgres")
.withPassword("postgres");
#BeforeClass
public static void setupEnv() {
System.setProperty("spring.datasource.url", sqlContainer.getJdbcUrl());
System.setProperty("spring.datasource.username", sqlContainer.getUsername());
System.setProperty("spring.datasource.password", sqlContainer.getPassword());
log.info("Running DB test with - " + sqlContainer.getJdbcUrl());
}
#After
#Transactional
public void truncateDb() {
List<String> tables = em.createNativeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'").getResultList();
for (String table : tables) {
em.createNativeQuery("TRUNCATE TABLE " + table + " CASCADE").executeUpdate();
}
}
}
#AutoConfigureMockMvc
public class TestUserAuthentication extends DBEnabledTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private UserRepository userRepository;
#Before
#Transactional
public void initDBForEachTestMethod() {
User testUser = new User();
testUser.setEmail("test#user.com");
testUser.setPassword(new BCryptPasswordEncoder().encode("testUser1"));
testUser.setFirstName("Jonh");
testUser.setLastName("Dough");
testUser.setRole(AppRole.USER);
userRepository.saveAndFlush(testUser);
}
#Test
#Transactional
public void test_authenticationSuccess() throws Exception {
ResponseEntity<String> res =
restTemplate.postForEntity(
"/api/user/login",
JsonUtil.objectBuilder()
.put("email", "test#user.com")
.put("password", "testUser1")
.toString(),
String.class
);
assertTrue(res.getStatusCode().is2xxSuccessful());
String body = res.getBody();
JsonNode node = JsonUtil.nodeFromString(body);
assertNotNull(node.get("id"));
assertNotNull(node.get("token"));
assertNotNull(node.get("refreshToken"));
assertNotNull(node.get("expiresAt"));
DecodedJWT decodedJWT = JWTUtil.verifyToken(node.get("token").asText(), jwtConfig.getSecret());
assertEquals("test#user.com", decodedJWT.getSubject());
assertEquals(AppRole.USER, AppRole.valueOf(decodedJWT.getClaim(JWTUtil.ROLE_CLAIM).asString()));
}
}
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
com.concentric.scenarios.domain.user.User applicationUser = userRepository.findByEmail(email);
// data from #Before not available here !!!
if (applicationUser == null || applicationUser.isDeleted()) {
throw new UsernameNotFoundException(email);
}
if(applicationUser.getPassword() == null) {
throw new IllegalAccessError();
}
return new org.springframework.security.core.userdetails.User(email, applicationUser.getPassword(), new ArrayList<>());
}
}

That is because in the tests transactions get rolled back by default.
The section of the documentation describing this behaviour also describe how to change it if needed by using the #Commit annotation:
By default, the framework creates and rolls back a transaction for each test. [...]
If you want a transaction to commit (unusual, but occasionally useful when you want a particular test to populate or modify the database), you can tell the TestContext framework to cause the transaction to commit instead of roll back by using the #Commit annotation.

Related

How to override a single application property in a test

I have this test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(AuthController.class)
#TestPropertySource("classpath:application.properties")
class AuthControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
AuthTokenFilter authTokenFilter;
#MockBean
AuthEntryPointJwt authEntryPointJwt;
#MockBean
JwtUtils jwtUtils;
#Autowired
private ObjectMapper objectMapper;
#MockBean
UserDetailsServiceImpl userDetailsServiceImpl;
#MockBean
AuthenticationManager authenticationManager;
#MockBean
Authentication authentication;
#MockBean
SecurityContext securityContext;
#Test
void test1withEnabledTrue() {
}
#Test
void test2WithEnabledTrue() {
}
#Test
void cannotRegisterUserWhenRegistrationsAreDisabled() throws Exception {
var userToSave = validUserEntity("username", "password");
var savedUser = validUserEntity("username", "a1.b2.c3");
when(userDetailsServiceImpl.post(userToSave)).thenReturn(savedUser);
mvc.perform(post("/api/v1/auth/register/").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(userToSave))).andExpect(status().isCreated())
.andExpect(jsonPath("$.status", is("registrations are disabled")));
}
private static UsersEntity validUserEntity(String username, String password) {
return UsersEntity.builder().username(username).password(password).build();
}
}
And this is the relevant part in Controller (Class Under Test):
#Value("${app.enableRegistration}")
private Boolean enableRegistration;
private Boolean getEnableRegistration() {
return this.enableRegistration;
}
#PostMapping("/register")
#ResponseStatus(HttpStatus.CREATED)
public Map<String, String> post(#RequestBody UsersDTO usersDTO) {
Map<String, String> map = new LinkedHashMap<>();
if (getEnableRegistration()) {
[...]
map.put("status", "ok - new user created");
return map;
}
map.put("status", "registrations are disabled");
return map;
}
I have this application.properties under src/test/resources and I need to override it, only for my single test named cannotRegisterUserWhenRegistrationsAreDisabled
app.enableRegistration=true
Probably I could use another file "application.properties" and another class test, but I'm looking for a smarter solution.
You can simply configure the inline properties of #TestPropertySource which has a higher precedence than the properties loaded from locations/ value :
#WebMvcTest(AuthController.class)
#TestPropertySource(locations = "classpath:application.properties" ,properties="app.enableRegistration=true" )
class AuthControllerTest {
}
All inline properties specified in the properties will override those specified in the application.properties
I think what you are looking for is the #TestProperty annotation, which was an answer of a question on Stackoverflow here. However this only works on class level, not for one test only.
You probably need to make a new test class and add the tests where it the value needs to be false.

Correct use of the EntityManager in an #Async call

I am trying to use the #Async capabilities of the Spring framework to perform a simple indexing task.
The problem I'm facing is that I feel that the EntityManager used in my Async function is somehow reused from previous calls so my data is not up to date and sometimes uses old data.
Here is the code I wrote as an example. The goal is to update a product's data and index it asynchronously after I publish an event using Spring's ApplicationEventPublisher:
ProductService
#Service
class ProductService {
private final EntityManager entityManager;
private final ApplicationEventPublisher eventPublisher;
#Autowired
public ProductService(EntityManager entityManager, ApplicationEventPublisher eventPublisher) {
this.entityManager = entityManager;
this.eventPublisher = eventPublisher;
}
#Transactional
public void patchProduct (String id, ProductDto productDto) {
Product product = this.entityManager.find(Product.class, id);
product.setLabel(productDto.getLabel());
this.entityManager.flush();
this.eventPublisher.publishEvent(new ProductEvent(product, ProductEvent.EVENT_TYPE.UPDATED));
}
}
EventListener
#Component
public class ProductEventListener {
private final AsyncProcesses asyncProcesses;
#Autowired
public ProductEventListener (
AsyncProcesses asyncProcesses
) {
this.asyncProcesses = asyncProcesses;
}
#EventListener
public void indexProduct (ProductEvent productEvent) {
this.asyncProcesses.indexProduct(productEvent.getProduct().getPok());
}
}
AsyncProcesses
#Service
public class AsyncProcesses {
private final SlowProcesses slowProcesses;
#Autowired
public AsyncProcesses(SlowProcesses slowProcesses) {
this.slowProcesses = slowProcesses;
}
#Async
public void indexProduct (String id) {
this.slowProcesses.indexProduct(id);
}
}
SlowProcesses
#Service
public class SlowProcesses {
private EntityManager entityManager;
private ProductSearchService productSearchService;
#Autowired
public SlowProcesses(EntityManager entityManager, NewProductSearchService newProductSearchService) {
this.entityManager = entityManager;
this.newProductSearchService = newProductSearchService;
}
#Transactional(readonly = true)
public void indexProduct (String pok) {
Product product = this.entityManager.find(Product.class, pok);
// this.entityManager.refresh(product); -> If I uncomment this line, everything works as expected
this.productSearchService.indexProduct(product);
}
}
As you can see on the SlowProcesses file, if I refresh the product object in the entityManager, I get the correct and up to date data. If I do not, I might get old data from previous calls.
What is the correct way to use the EntityManager in an Asynchronous call? Do I really have to refresh all my objects in order to make everything work? Am I doing something else wrong?
Thank you for reading through
Since instances of EntityManager are not thread-safe as pointed out by Jordie, you may want to try this instead:
Instead of injecting an EntityManager, inject an EntityManagerFactory. Then from the EntityManagerFactory retrieve a new EntityManager instance that is used only for the duration of the method in question.

Spring websocket test: repository does not see entity when rollbacked transaction is applied

I've faced with a situation when I was testing websocket endpoint and when I used rollbacked transaction to rollback all changes made during the test I've got an unpredictable behaviour.
I created an simple example to show us what is happenning.
User.java
#Entity(name = "USERS")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
// getters,setters ommitted
}
UserRepository.java
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
User getUserByName(String name);
}
UserService.java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public User getUser(String name){
return userRepository.getUserByName(name);
}
}
Websocket specific
#Configuration
#EnableWebSocket
public class Config implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(userHandler(), "/api/user");
}
#Bean
UserHandler userHandler(){
return new UserHandler();
}
}
UserHandler.java
public class UserHandler extends TextWebSocketHandler{
#Autowired
private UserService userService;
#Override
public void afterConnectionEstablished(WebSocketSession session) {
try {
HttpHeaders headers = session.getHandshakeHeaders();
List<String> userNameHeader = headers.get("user_name");
if (userNameHeader == null || userNameHeader.isEmpty()){
session.sendMessage(new TextMessage("user header not found"));
return;
}
String userName = userNameHeader.get(0);
User user = userService.getUser(userName);
if (user == null){
session.sendMessage(new TextMessage("user not found"));
return;
}
session.sendMessage(new TextMessage("ok"));
} catch (Throwable e) {
e.printStackTrace();
}
}
}
And eventually UserHandlerTest.java. I use okHttp3 as a websocket test client:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class UserHandlerTest {
#Autowired
private UserRepository userRepository;
private User testUser;
private BlockingQueue<String> messages = new LinkedBlockingQueue<>();
private OkHttpClient okHttpClient;
#Before
public void setUp(){
okHttpClient = new OkHttpClient();
testUser = new User();
testUser.setName("test");
userRepository.save(testUser);
}
#Test
public void testThatUserExist(){
Request request = new Request.Builder()
.url("ws://127.0.0.1:8080/api/user")
.header("user_name","test")
.build();
WebSocket ws = okHttpClient.newWebSocket(request,new MsgListener());
String msg = null;
try {
msg = messages.poll(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println(e);
}
Assert.assertNotNull(msg);
Assert.assertThat(msg,is("ok"));
}
private class MsgListener extends WebSocketListener{
#Override
public void onOpen(WebSocket webSocket, Response response) {
System.out.println("onOpen:"+response.message());
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("onMessage:"+text);
messages.add(text);
}
}
}
And my test aplication.properties:
spring.jpa.database=postgresql
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.username=postgres
spring.datasource.password=******
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/USERS_TEST
All the code above is ok and test is passed. But suppose I don't want to mess the db so I use #Transactional on all test methods to rollback all changes after the test is done.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class UserHandlerTest {
// the same as above
}
And then UserRepository does not find the user saved in setUp test method.
java.lang.AssertionError:
Expected: is "ok"
but: was "user not found"
I tried to reproduce the same situation on the rest endpoint and it works there. Let's see.
UserController.java
#RestController
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "/api/user")
public #ResponseBody User getUser(String name){
return userService.getUser(name);
}
}
And UserControllerTest.java:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class UserControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Autowired
private UserRepository userRepository;
private User testUser;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
testUser = new User();
testUser.setName("test");
userRepository.save(testUser);
}
#Test
public void testThatUserExist() throws Exception {
mockMvc.perform(get("/api/user")
.param("name","test")
).andExpect(status().isOk())
.andExpect(jsonPath("name").value(is("test")));
}
}
Last test is passed and the transaction is rolled back.
I want to see the same behaviour when I test websocket endpoint.
Could someone point me out where the differencies here? And Why Spring does it?
This is essentially a duplicate of Spring Boot #WebIntegrationTest and TestRestTemplate - Is it possible to rollback test transactions?.
I want to see the same behaviour when I test websocket endpoint.
You can't. When you declare #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT), Spring Boot starts an embedded Servlet container.
That's why there are two different transactions if you additionally annotate your tests with #Transactional:
test-managed
spring-managed
For your use case, you will need to stop using #Transactional in your test and start manually resetting the state of the database after your test completes. For that I'd recommend the use of #Sql with the AFTER_TEST_METHOD execution phase. See How to execute #Sql before a #Before method for an example.
Regards,
Sam (author of the Spring TestContext Framework)

#SpringBootTest causes #PostConstruct to be called twice

With Spring Boot of version 1.4 sone test annotations where deprecated "in favor of {#link SpringBootTest}".
Hovewer simply replacing deprecated annotations with new one, causes one strange side affect for me, #PostConstruct is called twice.
So lets assume that it was before
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {ApplicationTestContext.class})
#IntegrationTest
#Transactional
public class EmployeesControllerSpringTest {
#Inject
private EmployeeRepository employeeRepository;
#Inject
private VenueRepository venueRepository;
#Inject
private TestUtilDummyObjects dummyObjects;
#Inject
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Inject
private EntityManager entityManager;
#Inject
private ExceptionTranslator exceptionTranslator;
private MockMvc mockMvc;
#PostConstruct
public void postConstruct() {
EmployeesController sut = new EmployeesController(employeeRepository);
mockMvc = MockMvcBuilders
.standaloneSetup(sut)
.setControllerAdvice(exceptionTranslator)
.setMessageConverters(jacksonMessageConverter)
.build();
}
So, replasing deprecated annottions with the following
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {ApplicationTestContext.class})
#Transactional
causes #PostConstruct to be called twice.
Maybe someone can suggest, what am I doing wrong?
Please see ApplicationTestContext.class below:
//#TestConfiguration
//#SpringBootConfiguration
#ComponentScan(excludeFilters = {#ComponentScan.Filter(type = FilterType.REGEX,
pattern = "somepath.*")})
#EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
#EnableConfigurationProperties({ApplicationProperties.class, LiquibaseProperties.class})
#EnableAspectJAutoProxy(proxyTargetClass = false)
public class ApplicationTestContext extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateConverter(Constants.DATE_FORMAT));
registry.addConverter(new LocalDateTimeConverter(Constants.DATE_TIME_FORMAT));
}
}
UPDATE
Following #bhantol sugestion (get rid of all redundand annotations), I've tried to run official sample https://spring.io/guides/gs/spring-boot/.
Simply downloading sources and changing #Before to #PostConstruct still causing it to be called twice.
code from the sample is below:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
#LocalServerPort
private int port;
private URL base;
#Autowired
private TestRestTemplate template;
//#Before
#PostConstruct
public void setUp() throws Exception {
this.base = new URL("http://localhost:" + port + "/");
System.out.println("");
}
#Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(base.toString(),
String.class);
assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
}
}
Not sure, but seems like a potential bug.

Service class not updating database when called within Service Class

I am working on Java Spring MVC project. When the Controller calls the method in ServiceImpl class (updateAttempt()) which in turn calls DAOImpl class, the update happens and I see the updated data in DB.
But when the loadUserByUserName (which is present in ServiceImpl class) calls updateAttempt() method in same ServiceImpl class, it doesn't throw any error or exception, but data never gets updated in DB.
PersonController.java
#Controller
#SessionAttributes({ "mob_Number"})
public class PersonController implements Observer, InitializingBean{
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
private PersonService personService;
#Autowired(required=true)
#Qualifier(value="personService")
public void setPersonService(PersonService ps){
this.personService = ps;
}
public PersonController(PersonService personService){
this.personService = personService;
}
public PersonController(){
}
#RequestMapping(value="/submitVerificationCode",method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public String submitVerificationCode(#RequestBody String json){
......
this.personService.update_User_Verification_AttemptCount(userVer.getMobile_Number(), no_Attempts);
//this call updates the data in DB
}
}
PersonServiceImpl.java
#Service
public class PersonServiceImpl implements PersonService, UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(PersonServiceImpl.class);
private PersonDAO personDAO;
private PersonService personService;
public void setPersonDAO(PersonDAO personDAO) {
this.personDAO = personDAO;
}
#Autowired
private Observer observe;
#Override
#Transactional
public void update_User_Verification_AttemptCount(String mobile_number, int count){
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
}
#Override
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.update_User_Verification_AttemptCount(mobile_Number, no_Attempts); //but this call doesn't update the data in DB
this.getUserDetails() //but this call returns data from DB
}
PersonDAOImpl.java
#Repository
public class PersonDAOImpl implements PersonDAO {
private static final Logger logger = LoggerFactory.getLogger(PersonDAOImpl.class);
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void update_User_Verification_VerCode(String mob_number, String verCode, Timestamp currentTimestamp){
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("update UserVerification set ver_Code=:verCode, sent_Time=:currentTimestamp where mobile_Number=:mob_Number");
query.setParameter("verCode", verCode);
query.setParameter("currentTimestamp", currentTimestamp);
query.setParameter("mob_Number", mob_number);
query.executeUpdate();
session.flush();
}
}
NOTE: the get methods residing in ServiceImpl(which does select) also return values properly when the get methods called from loadUserByUsername.
That is beacause your transaction does not commit when you call the methods inside the same service.
The problem there is that Spring enriches the bean with transaction behaviour by wrapping the bean inside the proxy and adding the behaviour to it. The proxy however is always created around the interface, so calling a method with this keyword will not propagate the desired behaviour.
a proper solution would be to repeat the dao call so to avoid the call of the same service method
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
this.getUserDetails() //but this call returns data from DB
}
One other (hacky thing) that you can do is, since you already have a personService inside PersonServiceImpl, is to, first make sure that its injected, so add #Autowired
#Autowired private PersonService personService;
and than make a call through interface e.g.
personService.update_User_Verification_AttemptCount(mobile_Number, no_Attempts);
personService.getUserDetails()

Categories