I can't autowire Mapper from mapstruct in Tests. In Test with SpringRunner.class its ok, but when I try with MockitoJunitRunner its not possible.
#Mapper(componentModel = "spring", uses = {})
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
User mapToUser(UserDto userDto);
UserDto mapToUserDto(User user);
List<UserDto> mapToUserDtoList(List<User> userList);
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class nowyTest {
#Spy
private UserMapper userMapper;
private User createUser() {
return User.builder()
.firstName("Steve")
.lastName("Jobs")
.login("SteveJobs")
.password("password")
.role(UserRole.ROLE_ADMIN)
.build();
}
#Test
public void testMapper() {
User user = createUser();
UserDto userDto = userMapper.mapToUserDto(user);
System.out.println(userDto);
Assert.assertEquals(userDto.getFirstName(), "Steve");
}
}
It returns NPE :(
Have you tried to include the annotation #Import(yourClass)? in this case:
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(UserMapper.class)
public class nowyTest {
...
You can check this tutorial for a bit more information on how to start beans in SpringBoot tests.
Related
I am creating test cases, in one of my service class method I am using mapStruct to map entity into dto class.
This is my mapper class
#Mapper(componentModel = "spring")
public interface UserMapper {
List<UserDto> toUserDto(List<UserEntity> users);
}
below is how I am injecting in my service class
#Service
#RequiredArgsConstructor
public class UserServiceImpl implements UserService{
private final UserMapper userMapper;
This is how I am using it
List<UserDto> userDto = userMapper.toUserDto(lst);
this is how I am doing it in my Test class
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = Application.class)
public class ApplicationTest {
#Mock
private UserRepository userRepo;
#Mock
private UserMapper userMapper;
#InjectMocks
private UserServiceImpl userServiceImpl;
#Test
public void contextLoads() {
then(controller).isNotNull();
then(userServiceImpl).isNotNull();
}
#Test
public void getAllUser() {
List<UserEntity> lst = new ArrayList<UserEntity>();
UserEntity userOne = new UserEntity();
userOne.setEmpFullname("Test Test1");
userOne.setUserId("marina");
userOne.setFlag("Y");
UserEntity usertwo = new UserEntity();
usertwo.setEmpFullname("Test Test2");
usertwo.setUserId("test");
usertwo.setFlag("Y");
lst.add(userOne);
lst.add(usertwo);
when(userRepo.findByStatus("W")).thenReturn(lst);
try {
List<UserDto> pendingUsersList = userServiceImpl.getPendingUser();
assertEquals(2, pendingUsersList.size());
} catch (GeneralException e) {
e.printStackTrace();
}
}
}
when I am running my test cases I am able to see these 2 records in entity class but when this line executes
List<UserDto> userDto = userMapper.toUserDto(lst); it gives me blank array.
Note - In my entity Class I have many fields but from test class I am passing only 3 parameters.
You have annotated your UserMapper with a #Mock annotation, without writing the mockito configuration for this mock. Then the blank array is expected.
Remove the #Mock annotation, or specify what should be returned by the mock.
For example :
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = Application.class)
public class ApplicationTest {
#Mock
private UserRepository userRepo;
#Spy
private UserMapper userMapper = Mappers.getMapper(UserMapper.class);
#InjectMocks
private UserServiceImpl userServiceImpl;
Here is my code. I can't see why it is not working. The problem is with the line in test :
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
doesn't seem to be doing anything. The function findApplicationUserByUsername returns an empty optional when it should be returning an optional of userToReturnFromRepository.
Controller:
#RestController
#RequestMapping("api/v1/exercises")
public class ExerciseController {
#Autowired
ExerciseService exerciseService;
#GetMapping
public List<Exercise> getExercises() {
List<Exercise> exercises = exerciseService.getAllExercises();
return exercises;
}
}
Service:
#Service("exerciseService")
public class ExerciseService {
#Autowired
ExerciseRepository exerciseRepository;
#Autowired
ApplicationUserRepository applicationUserRepository;
#Transactional
public List<Exercise> getAllExercises() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
Optional<ApplicationUser> applicationUser = applicationUserRepository.findApplicationUserByUsername(principal.getName());
List<Exercise> exercises = new ArrayList<>();
if(applicationUser.isPresent()){
exercises = applicationUser.get().getExercises();
}
return exercises;
}
}
The test:
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc,
ApplicationUserRepository applicationUserRepository, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
this.applicationUserRepository = applicationUserRepository;
this.objectMapper = objectMapper;
}
#BeforeEach
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
#Test
#WithMockUser(username = "testUser")
public void testGetExercises() throws Exception {
Exercise ex = new Exercise();
ex.setData("test");
ApplicationUser user = new ApplicationUser();
Exercise[] exercises = {ex};
List<Exercise> list = new ArrayList<Exercise>(Arrays.asList(exercises));
user.setExercises(list);
Optional<ApplicationUser> userToReturnFromRepository = Optional.of(user);
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
mockMvc.perform(get("/api/v1/exercises")).andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
There are two conflicting things happening in your test:
You are using Mockito to init a mock implementation via reflection
You have ApplicationUserRepository being Spring injected into the Test class via the constructor.
What ends up happening is this:
spring injects applicationUserRepository into the constructor param
The applicationUserRepository field is set to the spring injected version in the constructor
Mockito inits a new applicationUserRepository mock
Mockito replaces the applicationUserRespository field with the mock (i.e. goodbye to your handle on the spring bean that your MVC setup is using!)
The easiest way I can think of to fix it is to use #MockBean instead of the #Mock combined with Constructor injection. #MockBean will instruct Spring to create the mock instance for you, use it, and provide it to you in the test.
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#MockBean // User MockBean instead of Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
//remove the applicationUserRepository injection
this.objectMapper = objectMapper;
}
// remove MockitoAnnotations.openMocks(this);
//...
...
}
I have problems with configuring tests for SpringJUnit4ClassRunner.class).
My problem is because my mapper from map struct returns null when reached.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
class UserServiceTestSuite {
#Spy
private UserDto userDto;
#Spy
private UserMapper userMapper;
#Mock
private UserRepository userRepository;
#InjectMocks
private UserService userService;
private User createUser() {
return User.builder()
.firstName("Steve")
.lastName("Jobs")
.login("SteveJobs")
.password("password")
.role(UserRole.ROLE_ADMIN)
.build();
}
#Test
public void testCreateUser() {
//Given
User user = createUser();
Mockito.when(userRepository.save(user)).thenReturn(user);
//When
UserDto userDto = userService.createUser(userMapper.mapToUserDto(user));
Long id = userDto.getId();
//Then
Assert.assertEquals("Steve", userDto.getFirstName());
Assert.assertEquals("Jobs", userDto.getLastName());
Assert.assertEquals("SteveJobs", userDto.getLogin());
Assert.assertEquals("ROLE_ADMIN", userDto.getRole());
}
In my opinion you have two options:
Inject the mapper via #SpringBootTest(classes = {UserMapperImpl.class}) and
#Autowired
private UserMapper userMapper;
Simply initialize the Mapper private UserMapper userMapper = new UserMapperImpl() (and remove #Spy)
When using the second approach you can even remove the #SpringBootTest because in the given snippet you do not need a Spring context (created by the annotation).
#RunWith(MockitoJUnitRunner.class) can be used to automatically inject objects annotated with #Mock into your UserService. Writing unit tests without creating a spring context helps to keep test suite execution time low.
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO userMapper.mapToUserDto(Object value);
}
Using this mapper interface you can instantiate from the mock and use the methods.
For example:
#Mock
private UserMapper userMapper;
Init the mocks:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
and you use the call INSTANCE
UserDto userDto = userService.createUser(userMapper.INSTANCE.mapToUserDto(user));
Im trying to test my Spring REST controller but my #Service is always trying to connect to DB.
Controller:
#RestController
#RequestMapping(value = "/api/v1/users")
public class UserController {
private UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.findAll();
if (users.isEmpty()) {
return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = webAppContextSetup(wac).build();
}
#Test
public void getAll_IfFound_ShouldReturnFoundUsers() throws Exception {
User first = new User();
first.setUserId(1);
first.setUsername("test");
first.setPassword("test");
first.setEmail("test#email.com");
first.setBirthday(LocalDate.parse("1996-04-30"));
User second = new User();
second.setUserId(2);
second.setUsername("test2");
second.setPassword("test2");
second.setEmail("test2#email.com");
second.setBirthday(LocalDate.parse("1996-04-30"));
UserService userServiceMock = Mockito.mock(UserService.class);
Mockito.when(userServiceMock.findAll()).thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/api/v1/users")).
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$", hasSize(2))).
andExpect(jsonPath("$[0].userId", is(1))).
andExpect(jsonPath("$[0].username", is("test"))).
andExpect(jsonPath("$[0].password", is("test"))).
andExpect(jsonPath("$[0].email", is("test#email.com"))).
andExpect(jsonPath("$[0].email", is(LocalDate.parse("1996-04-30")))).
andExpect(jsonPath("$[1].userId", is(2))).
andExpect(jsonPath("$[1].username", is("test2"))).
andExpect(jsonPath("$[1].password", is("test2"))).
andExpect(jsonPath("$[1].email", is("test2#email.com"))).
andExpect(jsonPath("$[1].email", is(LocalDate.parse("1996-04-30"))));
verify(userServiceMock, times(1)).findAll();
verifyNoMoreInteractions(userServiceMock);
}
}
My test always failure because instead getting first and second as return, it reads data from DB. If I turn off DB, it throws NestedServletException, nested: DataAccessResourceFailureException.
How can i test it properly? What am I doing wrong?
Mocking userService this way UserService userServiceMock = Mockito.mock(UserService.class); will not inject it into the controller. Remove this line and inject userService as follows
#MockBean UserService userServiceMock;
As #M.Deinum suggested you can remove manual creation of MockMvc and autowired it
#Autowired
private MockMvc mockMvc;
At the end your code should look like
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
#MockBean
UserService userServiceMock;
#Autowired
private MockMvc mockMvc;
#Test
public void getAll_IfFound_ShouldReturnFoundUsers() throws Exception {
User first = new User();
first.setUserId(1);
first.setUsername("test");
first.setPassword("test");
first.setEmail("test#email.com");
first.setBirthday(LocalDate.parse("1996-04-30"));
User second = new User();
second.setUserId(2);
second.setUsername("test2");
second.setPassword("test2");
second.setEmail("test2#email.com");
second.setBirthday(LocalDate.parse("1996-04-30"));
Mockito.when(userServiceMock.findAll())
.thenReturn(Arrays.asList(first, second));
mockMvc.perform(get("/api/v1/users")).
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$", hasSize(2))).
andExpect(jsonPath("$[0].userId", is(1))).
andExpect(jsonPath("$[0].username", is("test"))).
andExpect(jsonPath("$[0].password", is("test"))).
andExpect(jsonPath("$[0].email", is("test#email.com"))).
andExpect(jsonPath("$[0].email", is(LocalDate.parse("1996-04-30")))).
andExpect(jsonPath("$[1].userId", is(2))).
andExpect(jsonPath("$[1].username", is("test2"))).
andExpect(jsonPath("$[1].password", is("test2"))).
andExpect(jsonPath("$[1].email", is("test2#email.com"))).
andExpect(jsonPath("$[1].email", is(LocalDate.parse("1996-04-30"))));
verify(userServiceMock, times(1)).findAll();
verifyNoMoreInteractions(userServiceMock);
}
}
I have below Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfig.class)
public class UserServiceTest {
#Inject
private UserRepository userRepository;
#Inject
private UserService userService;
#Test
public void testProcessInvoice() throws SQLException {
User user = new User();
user.setFirstName("abc");
when(userRepository.save(any(User.class))).thenReturn(user);
Assert.assertNotNull(userService);
User savedUser = userService.save(user);
Assert.assertEquals("abc", savedUser.getFirstName());
}
}
I have below SpringTestConfig.java
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
call to User savedUser = userService.save(user); returns null user object. I am not able to figure it out why it is returning null.
EDIT:
UserRepository is JpaRepository, if this is a problem
public interface UserRepository extends JpaRepository<User, Long> {
}
Your UserService is a mock object, and has no defined behavior for dealing with the #save(User) method.
Mocking the object under test is probably not what you are after here. I would recommend your objects under test are instantiated in the test, and injected with the mocks or stubs of the objects that they utilize.
Your configuration needs to return a real UserService:
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return new UserServiceImpl(); // or whatever your implementation is
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
Mocks are for collaborators, not for the thing you're testing.