Junit test with mock for rest controller method - java

I have a actually create simple webservice and i want to add new junit test.
I have a method where I add people to teams.
This is my method(in rest controller):
#PostMapping("/addPeopleToTeams/{teamId}/{personId}")
#ResponseBody
public ResponseEntity<?> addPeopleToTeam(#PathVariable Long teamId, #PathVariable Long personId) {
TeamsAndPersonsId teamsAndPersonsId = new TeamsAndPersonsId(personId, teamId);
teamService.findTeamById(teamsAndPersonsId.getTeamId());
personService.findById(teamsAndPersonsId.getPersonId());
teamService.addPersonsToTeams(personId, teamId);
return ResponseEntity.ok(teamsAndPersonsId);
}
For this method i want to wrote a Junit test but something is going wrong:/
This is my test class with method:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class TeamControllerTest {
private ObjectMapper mappingObject = new ObjectMapper();
private MockMvc mockMvc;
#Mock
private TeamService teamService;
private PersonService personService;
#Before
public void initTest() {
mockMvc = MockMvcBuilders
.standaloneSetup(new TeamController(teamService, personService))
.setControllerAdvice(GlobalExceptionHandler.class)
.build();
}
#Test
public void shouldAddPersonToTeam() throws Exception{
TeamDto teamDto = prepareTeamDto();
PersonDto personDto = new PersonDto(1L, "Bob", "Noob", "mail#first.pl", "Warszawa", "APPS", "Developer");
doNothing().when(teamService).createTeam(teamDto);
doNothing().when(personService).addPerson(personDto);
mockMvc.perform(post("/addPeopleToTeams/{teamId}/{personId}",1,1))
.andExpect(status().isOk());
}
private TeamDto prepareTeamDto() {
return new TeamDto(1L, "Jan", "local", "wawa", 6);
}
Actually for this method i have this error:
org.mockito.exceptions.base.MockitoException:
Only void methods can doNothing()!
Example of correct use of doNothing():
doNothing().
doThrow(new RuntimeException())
.when(mock).someVoidMethod();
Above means:
someVoidMethod() does nothing the 1st time but throws an exception the 2nd time is called

doNothing() is used only for void methods, like is described in your error message, in your case you have to use something like:
when(teamService.createTeam(teamDto)).thenReturn(Reponse.ok().build());
Note: you have to override #equals method in your TeamDto class

Based on the error, I guess that TeamService.createTeam or PersonService.addPerson does not have void return type and you can mock doNothing behaviour only on methods, which returns void. Also you are missing #Mock annotation on PersonService field.
In spring boot tests, it is better to use #MockBean annotation. And for controller tests it is better to use #WebMvcTest, you can then skip the configuration done in your setup method:)

#InjectMocks
Controller controllerMock;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controllerMock).build();
}
where - ControllerMock will be Mock of Controller class

Related

how can i insert advanced data in spring boot test?

I'm making test code in spring boot.
But, my test code doesn't save the data using #Before method.
If i request to '/v1/stay/, it return empty array...
Please can you explain what is wrong with my code?
Here is my test code.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class StayControllerTest {
#MockBean
private StayService stayService;
#Autowired
private MockMvc mockMvc;
// givenStay method is the method generating dummy data
#Before
public void before() {
stayService.save(givenStay1());
stayService.save(givenStay2());
stayService.save(givenStay3());
stayService.save(givenStay4());
stayService.save(givenStay5());
}
#Test
#Transactional
void showStayList() throws Exception {
List<StayReq> original = new ArrayList<>();
original.add(givenStay1());
original.add(givenStay2());
original.add(givenStay3());
original.add(givenStay4());
original.add(givenStay5());
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/v1/stay")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(result.getResponse());
}
}
And below code blocks are my StayController and StayService
#RestController
#ApiV1
#RequiredArgsConstructor
public class StayController {
private final StayService stayService;
private final ApiService apiService;
#GetMapping("/stay")
public ResponseEntity<Response> stayList() {
return apiService.okResponse(stayService.getList());
}
}
#Service
#RequiredArgsConstructor
public class StayService {
private final StayRepository stayRepository;
private final RoomRepository roomRepository;
public List<StayRes> getList() {
return stayRepository.findAll().stream().map(StayRes::new).collect(Collectors.toList());
}
#Transactional
public void save(StayReq stayReq) {
stayRepository.save(stayReq.toEntity());
}
}
You injected a mock, not a 'real' service. If you want to use a 'real' service - you need to replace #MockBean annotation with #Autowired annotation.
Or alternatively - you can configure mock in the test method to return some predefined data.

Unit tests using Mockito

I'm new to unit tests and well, I'm struggling to understand how it should be done.
I have some methods with #RequestParam and I'm not sure how to mock this.
It would be great if I had an example I could apply to the other methods.
Could you help me by writing the test method for this one? It would be very useful.
#PutMapping("/player/update-password")
public ResponseEntity<?> updatePlayerPassword(#RequestParam("token") String token, #RequestParam("password") String newPassword) throws URISyntaxException {
String message = bundle.getString("put_player");
log.debug(message, token, newPassword);
PlayerEntity player = playerService.updatePassword(token, newPassword);
return new ResponseEntity<>(PlayerMapper.INSTANCE.mapPlayer(player), HttpStatus.ACCEPTED);
}
Thanks.
You can use #Mock and #InjectMocks annotations. With it you can inject mocks to your controller.
#Mock
PlayerService playerServiceMock;
And use when().then() from Mockito inside test or method with #Before annotation:
when(playerServiceMock.updatePassword(anyString(), anyString())).thenReturn(playerEntity);
The same can be done for bundle.getString("put_player").
Hoping you are using the Mockito. You can try the below code, need to add all imports
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class YourClassNameTest {
#Autowired
MockMvc mockMvc;
#InjectMocks
YourClassName yourClassName;
#Mock
PlayerService playerService;
#Before
public void Setup() {
mockMvc = MockMvcBuilders.standaloneSetup(yourClassName);
}
#Test
public void updatePlayerPasswordTest() throws Exception {
PlayerEntity player = new PlayerEntity();
// set some date if you want
Mockito.when(playerService.updatePassword(anyString(), anyString())).thenReturn(player);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/player/update-password?token=abc&password=test")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder).andExpect(MockMvcResultMatchers.status().isCreated());
}
}

Mockito when().thenReturn() Returning Null when it should return empty list

I've been trying to figure out why my mocked findIngredientsByCategory method is returning null when I have when(controller.findIngredientsByCategory(any()).thenReturn(Collections.emptyList()). This implementation works for the findAll method works.
Below is my implementation for my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientController ingredientController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Autowired
private ObjectMapper mapper;
private static class Behavior {
IngredientController ingredientController;
public static Behavior set(IngredientController ingredientController) {
Behavior behavior = new Behavior();
behavior.ingredientController = ingredientController;
return behavior;
}
public Behavior hasNoIngredients() {
when(ingredientController.getAllIngredients()).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientsByCategory(any())).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientById(anyString())).thenReturn(Optional.empty());
return this;
}
}
#Test
public void getIngredientsByCategoryNoIngredients() throws Exception {
Behavior.set(ingredientController).hasNoIngredients();
MvcResult result = mvc.perform(get("/ingredients/filter=meat"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andReturn();
String content = result.getResponse().getContentAsString();
System.out.println(content);
}
And below is the implementation for the controller:
#RestController
#RequestMapping("/ingredients")
public class IngredientController {
#Autowired
private IngredientRepository repository;
#RequestMapping(value = "/filter", method = RequestMethod.GET)
public List getIngredientsByCategory(#RequestParam("category") String category) {
return repository.findByCategory(category);
}
}
I'm not sure why the mock controller is returning null with this request, when I tell it to return an empty list. If someone could please help with this I would greatly appreciate it! Thanks.
Th request path in test is "/ingredients/filter=meat", but it should be "/ingredients/filter?category=meat". So, it seem that getIngredientsByCategory was not called.
The MockMvc actually will call the IngredientController that is bootstrapped and created by the Spring Test framework but not call the mocked IngredientController that you annotated with #MockBean, so all the stubbing that you made will not be called.
Actually, the point of #WebMvcTest is to test #RestController and its related Spring configuration is configured properly , so a real instance of IngredientController is necessary to create rather than using a mocked one. Instead , you should mock the dependencies inside IngredientController (i.e IngredientRepository).
So , the codes should looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientRepository ingredientRepository;
#Test
public void fooTest(){
when(ingredientRepository.findByCategory(any()).thenReturn(Collections.emptyList())
//And use the MockMvc to send a request to the controller,
//and then assert the returned MvcResult
}
}

RestAssured with Mockito: mock dao repository

I am trying to test my REST endpoints using RestAssured with mocking some of the service/repositories in the controller.
this is my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {VedicaConfig.class})
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class RESTTest {
#LocalServerPort
private int port;
#Autowired
private MockMvc mvc;
#Mock
MetaVersionDAO metaVersionDAO;
#InjectMocks
DocCtrl docCtrl;
#Before
public void contextLoads() {
RestAssured.port = port;
assertThat(mvc).isNotNull();
// this must be called for the #Mock annotations above to be processed.
MockitoAnnotations.initMocks(this);
RestAssuredMockMvc.standaloneSetup(MockMvcBuilders.standaloneSetup(docCtrl));
}
#Test
public void shouldGetThumbnail() {
String ver = "1.0";
String uuid = "124-wqer-365-asdf";
when(metaVersionDAO.getMetaByVersionUUID(ver, uuid)).thenReturn(new DocVersion());
given()
.when()
.param("uuid", uuid)
.param("versionVed", ver)
.get(CTX_BASE + "/thumbnail")
.then()
.log().ifValidationFails()
.statusCode(OK.value())
.contentType(ContentType.BINARY);
}
}
now, the REST endpoint itself is being hit correctly with supplied parameters. this endpoint has DocCtrl injected which uses metaVersionDAO instance in turn:
public RawDocument getDocThumbnail(String uuid, String versionVed) throws Exception {
DocVersion docVersion = metaVersionDAO.getMetaByVersionUUID(versionVed, uuid);
InputStream inputStream = okmWebSrv.getOkmService().getContentByVersion(uuid, versionVed);
String dataType = docVersion.getMetadata().getAdditionals().get(Vedantas.CONTENT_TYPE);
ByteArrayInputStream bais = new ByteArrayInputStream(createPDFThumbnail(dataType, inputStream));
RawDocument rawDocument = new RawDocument(bais, "qwer");
return rawDocument;
}
as you can see, I have tried to mock metaVersionDAO at the top of the #Test method so I expected it to return new DocVersion() as I set it to, but in this DAO the actual code is being called and it fails on entityManager which is null.
My question is why metaVersionDAO.getMetaByVersionUUID doesn't return my mocked object and what should I do to make it so?
spring-mock-mvc: 3.3.0
spring-boot: 2.1.2.RELEASE
Thanks!
solved by changing #Mock for #MockBean.
so it is:
#MockBean
MetaVersionDAO metaVersionDAO;
everything else remains the same as in the post and it uses mocked instance.

Should we test getForObject?

I am writing a simple REST client, my service class has one method using
RestTemplate.getForObject()
I would like to practice testing but I don't really know if we should test the class. I also do not know how to test method that does not do much. Should I write any unit tests?
You can test, that request has been sent. For example you have the next service:
#Service
public void QueryService {
private final RestTemplate restTemplate;
#Autowired
public QueryService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public List<Employee> void makeQuery() {
ResponseEntity<List<Employee>> response = restTemplate.getForObject(
"http://localhost:8080/employees",
EmployeeList.class);
return response.getEmployees();
}
}
For the method makeQuery you can write the next unit test:
#RunWith(MockitoJUnitRunner.class)
public class QueryServiceTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private QueryService queryService;
#Test
public void testMakeQueryRequestHasBeenSent() {
List<Employee> result = queryService.makeQuery();
// This string checks that method getForObject has been invoked
verify(restTemplate).getForObject(anyString(), any(Class.class));
}
}
In the future, if you will change makeQuery method and forget to send request using restTemplate, this test will fail.

Categories

Resources