Test Spring v5 with Junit v5, always got error #Autowired DAO as NullPoint - spring

In NetBeans IDE v14, I created a Spring v5 as Maven web app called User, it is running well connected via UserDAO to my local MySQL DB.
But now when today I added Junit 5 to test DAO, and web Controller, I am having issue in the test class at:
#Autowired private UserDAO userDao;
userDao always null, so I got NullPoint error.
With my POM.xml file, I did try JUnit 5, also JUnit 4, and different
versions of Spring, Hibernate xml, and JPA. Still getting the same
error: DAO field null.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/olap-servlet.xml")
#ComponentScan(basePackages = {"ca.i5i.user"})
public class UserDAOTest {
private User user;
private final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private UserDAO userDao;
#BeforeEach
public void setUp() throws Exception {
user = new User();
userDao = new UserDAOImpl();
}
#Test
public void testLoginUser(#Autowired UserDAO userDao) throws Exception {
user.setUserName("u743");
user.setPassword("pass743");
logger.info("testLoginUser info: FirstName=" + user.getUserName() + ", LastName = " + user.getPassword() + ";");
logger.info("testLoginUser userDao=" + userDao);
User loginUser = userDao.loginUser(user);
assertNotNull(loginUser.getId());
}

Related

Spring boot test, #WithMockUser produce NPE in Controller method

i have an issue with my controller test
So my base test class configured like so
#SpringBootTest
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
public abstract class BaseControllerTest extends DatabaseIT {
protected static long counter;
protected MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
protected ObjectMapper objectMapper;
protected MockRestServiceServer restServiceServer;
#Autowired
RestTemplate restTemplate;
#BeforeEach
protected void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
restServiceServer = MockRestServiceServer.createServer(restTemplate);
}
and test code
mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json(s));
When i debugging my controller method which is
public ResponseEntity<?> getSomething (
#Parameter(description = "ID") final String id,
#ApiIgnore #AuthenticationPrincipal Authentication user){
////
}
user value is null, though when i invoke SecurityContextHolder.getContext().getAuthentication() here in controller i'm getting Authentication object that's refers to one i mocked in
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
So recently i've updated my project to Java 17 , that also forced me to update spring boot to
version: '2.5.5'
and spring cloud to
"org.springframework.cloud:spring-cloud-dependencies:2020.0.5"

How to inject MockMvc with multiple objects in Spring Boot REST Junit test case

I have a SpringBoot REST API connecting to Oracle DB.
My controller calls BusinessImpl layer and in-turn BusinessImpl calls multiple DAO layers (Controller -> BusinessImpl -> DAO1, DAO2, DAO3)
The below test case works perfectly
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration()
#TestPropertySource("classpath:dev-manifest.yml")
#ContextConfiguration(classes = Application.class)
#ConfigurationProperties(prefix = "env")
#SpringBootTest
public class MyTest
{
private static final String REQUEST_URI = "/v1/registration/accounts/links";
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setup()
{
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testSave()
{
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
But I do not want to hit real database during junit test case. So I wrote a mock.
Working Code
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
SomeRequest someRequest = new SomeRequest();
someRequest.setStatus("SUCCESS");
SomeResponse status = registrationBusinessImpl.createUser(SomeRequest, "127.0.0.1");
}
The above code worked perfectly. In businessImpl class securityAuditDAO.getState returned "somestring". But when I introduced mockMvc.perform it stopped working.
Not Working
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
The above code was still hitting the database. So I realized that I should inject mockMvc with securityAuditDAO so I added the following line
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
Code
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#InjectMocks
RegistrationApiController registrationApiController;
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
//this.mockMvc = webAppContextSetup(webApplicationContext).build();
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
//this.mockMvc = MockMvcBuilders.standaloneSetup(registrationApiController).build();
//ReflectionTestUtils.setField(mockMvc, "securityAuditDAO", securityAuditDAO);
}
I tried injecting securityAuditDAO. But if I do that, my other autowired instances in BusinessImpl were null.
How to inject securityAuditDAO without affecting others or how to inject both webApplicationContext and securityAuditDAO.
Also tried ReflectionTestUtils.setField but it didn't work as expected.

MockBean Tests and Multiple Consumers\App Contexts with Spring AMQP

I'm currently facing an issue with testing RabbitMQ consumers with mocks. The issue seems to be that one test class runs with an application context without any mocks, as expected. The next test class to run sets up some mocks that it expects the consumers to use, however when the test runs and a message is sent and it gets picked up by the non-mocked consumers from the application context created for the first test class. As a result my second test fails.
Here is the first test:
#SpringBootTest
public class DemoApplicationTests extends AbstractTestNGSpringContextTests {
#Autowired
private RabbitAdmin rabbitAdmin;
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Test(priority = 1)
public void contextLoads() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
}
}
Second test:
#SpringBootTest
public class UserServiceTests extends AbstractTestNGSpringContextTests {
#Autowired
private UserService userService;
#Autowired
private UserMessageConsumer userMessageConsumer;
#MockBean
#Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
#Autowired
private UserRepository userRepository;
#Autowired
private RabbitAdmin rabbitAdmin;
#Test(priority = 2)
public void createUpdateUserTest() {
logger.info("=============== CONSUMERS: " + rabbitAdmin.getQueueProperties(USER_MESSAGING_QUEUE).get(RabbitAdmin.QUEUE_CONSUMER_COUNT));
String additionalData = org.apache.commons.lang3.RandomStringUtils.random(5);
Mockito.when(thirdPartyUserDataClient.getAdditionalUserData(ArgumentMatchers.anyLong())).thenReturn(additionalData);
User user = new User();
user.setName("Test User");
user.setState(UserState.PENDING);
user = userService.createUser(user);
Assert.assertNotNull(user.getId());
User finalUser = user;
Awaitility.await().until(() -> {
User user2 = userService.getUserById(finalUser.getId());
return finalUser != null && additionalData.equals(user2.getAdditionalData());
});
user.setState(UserState.CREATED);
user = userService.updateUser(user);
Assert.assertEquals(UserState.CREATED, user.getState());
}
}
The consumer:
#Component
public class UserMessageConsumer {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String FAILED_TO_GET_ADDITIONAL_DATA = "FAILED_TO_GET_ADDITIONAL_DATA";
#Autowired
private UserService userService;
#Autowired
private ThirdPartyUserDataClient thirdPartyUserDataClient;
public void handleUserCreatedMessage(UserCreatedMessage userCreatedMessage) {
Long userId = userCreatedMessage.getUserId();
User user = userService.getUserById(userId);
if (user != null) {
String additionalData;
try {
additionalData = thirdPartyUserDataClient.getAdditionalUserData(userId);
logger.info("Successfully retrieved additional data [{}] for user [{}].", additionalData, userId);
} catch (HttpClientErrorException ex) {
additionalData = FAILED_TO_GET_ADDITIONAL_DATA;
logger.warn("Failed to retrieve additional data for user [{}].", userId, ex);
}
user.setAdditionalData(additionalData);
userService.updateUser(user);
}
}
}
This brings up two related questions:
How am I supposed to properly do mock bean testing with consumers in
Spring?
It looks like Spring is bringing up a new a
ApplicationContext for each test class, indicated by the consumer count increasing on the subsequent test runs. It appears
that #MockBean affects the cache key of the ApplicationContext (see:
Mocking and Spying Beans in Spring Boot) and likely explains why there are multiple application contexts.
But how do I stop the consumers in the other stale application contexts from
consuming my test messages?
I've bugjar'd this issue here: RabbitMQ MockBean BugJar
Add #DirtiesContext to each test class to shut down the cached context.

#Autowired Bean is NULL in Spring Boot JUnit Test

I'm trying to write the unit test case for the below file.
RoleDataController.Java
#RestController
#RequestMapping("/updateRoleData")
public class RoleDataController extends ControllerBase {
#Autowired
public EntityManager entityManager;
public Session session = entityManager.unwrap(Session.class);
RoleData _roleData = new RoleData();
#RequestMapping("/getRoleData")
public String findRoleData(){
List roleList =_roleData.findRoleData(session,123456);
return JsonHelper.toJson(roleList);
}
}
RoleDataControllerTest.java
public class RoleDataControllerTest {
RoleData _roleData = new RoleData();
#Autowired
public EntityManager entityManager;
public Session session = entityManager.unwrap(Session.class);
#Test
public void findRoleData() throws Exception {
List roleList =_roleData.findRoleData(session, 123456);
Assert.assertNotNull(roleList);
}
}
I'm getting NullPointerException in the below line
public Session session = entityManager.unwrap(Session.class);
Please help to fix this.
Unit tests don't start the Spring context and so the #Autowired annotation won't work - no dependency injection will be done by Spring. You either want to write an integration test or mock the behaviour of other beans.

Spring boot and Security Integration test with EntityManager

I want to test my spring application. It requires authentication, so I create an user object and persist it in #Before method. But i can not do authentication because, as i think, init() method is executed in another session.
IntegrationTest class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
#Transactional
public class IntegrationTest {
#PersistenceContext
private EntityManager entityManager;
#Autowired
private PasswordEncoder passwordEncoder;
#LocalServerPort
int port;
private String URL;
#Before
public void init() {
User user = new User();
user.setUsername("testUser");
user.setPassword(passwordEncoder.encode("test"));
user.setEmail("test#test.com");
user.setEnabled(true);
entityManager.persist(user);
entityManager.flush();
entityManager.clear();
RestAssured.port = port;
URL = "http://localhost:" + String.valueOf(port) + "/users/user";
}
#Test
public void givenNotAuthenticatedUser_whenLoggingIn_thenCorrect() {
final RequestSpecification request = RestAssured.given().auth().basic("testUser", "test");
request.when().get(URL).then().assertThat().statusCode(200);
}
}
But if I use my userRepository and call
userRepository.save(user);
instead of
entityManager.persist(user);
entityManager.flush();
entityManager.clear();
everything works fine. I also have to remove #Transactional annotation.
Firstly I thought it was because, there was no commit - I saw that there is no changes in the user table. How to force EntityManager to commit data?
How to use EntityManager in test? And why UserRepository does work well?
As stated in the documentation:
By default, the framework will create and roll back a transaction for each test.
You can override it using #Commit on your test class and put TestTransaction.end() after entityManager.clear().
Now the test works ok but I still can not understand why and how UserRepository works without commiting the transaction.

Resources