Understanding #MockMvcTest And How to Import all contexts needed - java

I have a test class using #WebMvcTest that looks like this
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#WebMvcTest({HomeController.class, ShoppingCartController.class})
#Import({SecurityConfig.class, SecurityUtility.class, UserDetailsServiceImpl.class, UserRepository.class}) //#TestPropertySource("classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BookService bookService;
And now I'm getting a lot of unsatisfied bean dependency exceptions because #WebMvcTest uses slicing, My question is how can I import all the dependency without repeating #Import 20 times and how do I use #MockBean?
Previously I used
#AutoConfigureMockMvc
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = BookstoreApplications.class, properties = "classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowire
private MockMvc mockMvc
#Autowired
private HomeController homeController;
#Autowired
private ShoppingCartController shoppingCartController;
And all tests were passed now I am getting a default password in the log , which means I haven't imported the Authentication Context so it used Default Authentication.
Below is the whole class as it is now
AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#WebMvcTest({HomeController.class, ShoppingCartController.class})
#Import({SecurityConfig.class,UserRepository.class}) //#TestPropertySource("classpath:application.properties")
public class ShoppingCartControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SecurityUtility securityUtility;
#MockBean
UserDetailsServiceImpl userDetailsService;
#MockBean
private BookService bookService;
#MockBean
private UserService userService;
#MockBean
private CartItemService cartItemService;
#Configuration
#Import({PropertyTestConfiguration.class})
static class ContextConfiguration {
}
#Test
public void showLoginPage() throws Exception {
mockMvc.perform(get("/login")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
)
.andExpect(model().attributeExists("classActiveLogin"))
.andReturn();
}
#Test
#WithMockUser(username = "V", authorities = {"USER"})
public void addItemToShoppingCart() throws Exception {
CartItem cartItem = new CartItem();
String qty = "2";
Book book = new Book();
User user = new User();
book.setId(1L);
book.getId();
cartItem.setBook(book);
when(userService.findByUsername(anyString())).thenReturn(user);
when(bookService.findOne(anyLong())).thenReturn(book);
when(cartItemService.addBookToCartItem(book, user, Integer.parseInt(qty))).thenReturn(cartItem);
ObjectMapper mapper = new ObjectMapper();
String bookAsString = mapper.writeValueAsString(book);
mockMvc
.perform(get("/shoppingCart/addItem")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
.param("book", bookAsString)
.param("qty", qty))
.andReturn();
}
#Test
public void checkBookDetail() throws Exception {
Book book = new Book();
book.setId(1L);
when(bookService.findOne(anyLong())).thenReturn(book);
mockMvc
.perform(get("/bookDetail")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
.param("id", "1"))
.andExpect(model().attributeExists("book"))
.andExpect(model().attributeExists("qty"))
.andExpect(model().attributeExists("qtyList"))
.andReturn();
}
#Test
public void showBookShelf() throws Exception {
List<Book> bookList = new ArrayList<>();
when(bookService.findAll()).thenReturn(bookList);
mockMvc.perform(get("/bookshelf")
.accept(MediaType.TEXT_HTML)
.contentType(MediaType.TEXT_HTML)
)
.andExpect(model().attributeExists("activeAll"))
.andReturn();
}
}
I also think its wrong to use #Import because I am testing two classes as I have specified in #WebMvcTest, every other bean should use #MockBean. And also I am not allowed to use #SpringBootTest And for some reason test addItemToShoppingCart passes while others fail

Related

Test mocking return empty body

I am trying to make testings for my user controller but when I run the test it fails because the response body is empty.
Note that the response code is 200 so i dont see where this problem comes from...
#WebMvcTest(controllers = {UserRestController.class})
public class UserControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private UserService service;
private MockMvc mockMvc;
private User user1;
private User user2;
#BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
this.user1 = User.builder()
.email("doe.jean#mymel.com")
.username("Djin")
.build();
this.user2 = User.builder()
.email("doe.john#mymel.com")
.username("Jodo")
.build();
}
#Test
public void test_shouldReturnAllUsers() throws Exception {
Mockito.when(service.getUsers(Pageable.unpaged())).thenReturn(new PageImpl<>(Arrays.asList(user1, user2)));
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(APIPaths.V1_USER_BASE + "/all")
.accept(MediaType.APPLICATION_JSON))
.andExpect(request().asyncStarted())
.andReturn();
mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().is(200))
.andExpect(jsonPath("$.size()", Matchers.is(2)))
.andDo(print());
}
}

Mocked service function not working as expected

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);
//...
...
}

Web Layer Unit Test and Integration Test in Spring 5.0.7.RELEASE

I have a SpringBoot app. with this test, but it does not inject and mock the classes
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
})
public class TerritoriClandestiControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
TerritoriClandestiRepository territoriClandestiRepository = mock(TerritoriClandestiRepository.class);
#InjectMocks
private TerritoriClandestiService territoriClandestiService;
List<Object[]> list;
Resource listResource = new ClassPathResource("list.txt");
#Before
public void setup() throws IOException {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.build();
list = DataLoader.readLines(listgetInputStream());
}
#Test
public void getAll() throws Exception {
when(territoriClandestiRepository.findAllBaseData(anyLong())).thenReturn(list);
mockMvc.perform(get("/terrcland")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
You can use #MockBean instead of #Mock, it will export the field as a bean in the spring context.
public class TerritoriClandestiControllerTest {
#MockBean
private TerritoriClandestiRepository territoriClandestiRepository;
}
Alternatively you can also do something like this
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
}, classes = {TerritoriClandestiControllerTest.Config.class})
public class TerritoriClandestiControllerTest {
#TestConfiguration
static class Config {
#Bean
TerritoriClandestiRepository territoriClandestiRepository() {
return Mockito.mock(TerritoriClandestiRepository.class);
}
}
#Autowired
private TerritoriClandestiRepository territoriClandestiRepository;
}

How to inject mapper (mapstruct) to Junit test with SpringJUnit4ClassRunner

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));

Mocked #Service connects to database while testing

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);
}
}

Categories