So I am trying to make a test for the post method of my controller, but I keep finding tests from other people that do not work on my or their post methods are way more advanced.
My post method
#Autowired
PartyLeaderService partyleaderService;
#PostMapping("/")
public void add(#RequestBody PartyLeaderDto partyLeaderDto){
partyLeaderService.savePartyLeader(partyLeaderDto);
}
As you can see it is fairly simple, but I still can not seem to get it to work properly.
I tried this method:
#Test
public void testPostExample() {
PartyLeaderDto partyLeaderDto = new PartyLeaderDto(1, "arun", "link");
partyLeaderController.add(partyLeaderDto);
Mockito.verify(partyLeaderController).add(partyLeaderDto);
}
But SonarQube is saying that the method is still not covered by tests, so my test must be wrong.
My DTO
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Builder
public class PartyLeaderDto {
private Integer id;
private String name;
private String apperance;
}
Can anyone help me, because I think the answer is fairly simple, but I can't seem to find it.
The issue with your unit test is that you're verifying a call over something that is not a mock
Mockito.verify(partyLeaderController).add(partyLeaderDto);
partyLeaderController is not a mock as this is what you're looking to test at the moment.
What you should be mocking and verifying is PartyService.
#Test
public void testPostExample() {
PartyService partyService = mock(PartyService.class);
PartyLeaderController controller = new PartyLeaderController(partyService);
PartyLeaderDto partyLeaderDto = new PartyLeaderDto(1, "arun", "link");
controller.add(partyLeaderDto);
Mockito.verify(partyService).saveParty(partyLeaderDto);
}
However this is not a very good approach to test the web layer, as what you should be looking to cover with your test is not only whether PartyService is called but also that you're exposing an endpoint in a given path, this endpoint is expecting the POST method, it's expecting an object (as json perhaps?) and it's returning a status code (and maybe a response object?)
An easy, unit test-like approach to cover this is to use MockMvc.
#Test
public void testPostExample() {
PartyService partyService = mock(PartyService.class);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PartyLeaderController(partyService)).build();
PartyLeaderDto partyLeaderDto = new PartyLeaderDto(1, "arun", "link");
mockMvc.perform(
post("/party")
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(partyLeaderDto))
).andExpect(status().isOk());
verify(partyService).saveParty(partyLeaderDto);
}
What you are doing here is actually a unit test by verifying a stub call.
Either you can unit test by mocking the behaviour using Mockito's when(....).then(...) and then using assertion to assert the results.
or
You can follow this article to use a MockMvc to do an integration test step by step-
https://www.baeldung.com/integration-testing-in-spring
With the help of Yayotron I got to the answer.
This is the code:
#Test
#WithMockUser("Test User")
public void testPostParty() throws Exception {
PartyLeaderService partyLeaderService = mock(PartyLeaderService.class);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PartyLeaderController(partyLeaderService)).build();
PartyLeaderDto partyLeaderDto = new PartyLeaderDto(1, "arun", "link");
mockMvc.perform(
MockMvcRequestBuilders.post("http://localhost:8080/partyleaders/")
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(partyLeaderDto))
).andExpect(status().isOk());
Mockito.verify(partyLeaderService).savePartyLeader(ArgumentMatchers.refEq(partyLeaderDto));
}
Thanks a lot for helping!
Related
I'm using MockMvc to test my method that has a date parameter. The parameter is annotated with #DateValid but MockMvc doesn't trigger the validator and returns success when I input a wrong date.
StudentControler
public ResponseEntity<?> getStudents(#PathVariable("id") String studentId,
#RequestParam("birthDate") #DateValid String Date) {
//do something
}
Test
public class StudentController extends AbstractControllerTest {
#Test
public void testStudents_validRequest() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("birthDate","aaaa");
MvcResult result = getMvc().perform(get("/student/{studentId}", "test")
.params(params)
.characterEncoding(StandardCharsets.UTF_8.name())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapToJson(studentResDto)))
.andReturn();
verify(studentService).getStudentId(anyString(), anyString());
log(log, result);
}
}
I do not understand why because when I test using Postman it works as expected. Postman does validate but MockMvc does not?
I using Spring-Boot 2.1.3
I can think of 2 problems here:
The validation annotation class is not loaded properly in your test. This seems to happen primarily when combining Spring validation with Hibernate validation (see also this question on how to fix that.
Your test is not setup properly. Could it be that you are not fully loading your controller in your mock test? Perhaps you are mocking the controller accidentally?
I want to mock an external API, which I am calling as part of my service. Therefore, I wanted to use the MockWebServer from okhttp3. My problem is that the call to bodyToMono works fine, if I want to retrieve the body as string, but does not work when retrieving it as data class. I tried to trim it down using the following code snippet:
public class MockWebClientTest {
private MockWebServer server;
#BeforeEach
public void setUp() throws IOException {
server = new MockWebServer();
server.start(9876);
}
#AfterEach
public void tearDown() throws IOException {
server.shutdown();
}
#Test
public void stringWorks() throws JsonProcessingException {
createMockedTokenCall();
Mono<String> response = WebClient.create(this.server.url("/").toString())
.get()
.uri("/")
.retrieve()
.bodyToMono(String.class);
System.out.println(response.block());
}
#Test
public void classDoesNotWork() {
createMockedTokenCall();
Mono<AuthToken> response = WebClient.create(this.server.url("/").toString())
.get()
.uri("/")
.retrieve()
.bodyToMono(AuthToken.class);
System.out.println(response.block());
}
private void createMockedTokenCall() {
server.enqueue(new MockResponse().setBody("{\"accessToken\":\"BARBAR\",\"refreshToken\":\"FOOFOO\"}"));
}
}
class AuthToken {
private String accessToken;
private String refreshToken;
//constructor
}
The first Test (stringWorks) is working fine and return the correct json representation. However, the second test (classDoesNotWork) hangs forever on the bodyToMono call.
My guess is that it has nothing to do with the okttp3 library directly, since I had the same error using Wiremock. The same code works however when targeting a real API endpoint. Unfortunately, I could not find another way to test my calls using WebClient since Spring currently has no direct mocking support for it (see SPR-15286).
I am really looking forward to help on that matter! Thanks in advance!
General remark: This is basically a more or less copy of test case shouldReceiveJsonAsPojo in https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java
Ok, after looking at the linked test and doing a bit of comparison, I found the solution (or my bug so to say): I forgot the correct Content-Type header. So it works using the following:
private void createMockedTokenCall() {
server.enqueue(new MockResponse().setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE).setBody("{\"accessToken\":\"BARBAR\",\"refreshToken\":\"FOOFOO\"}"));
}
Apart from me doing a mistake, I really think that this should not result in an infinite hanging of the application...
So I'm writing this web app with Spring Boot using Spring Data with JPA and Spring MVC and I would like to make mock controller tests. I figured out how to test the get method, but in my controllers post method a new JPA entity is being either persisted or updated with my service. Here is what my controller looks like:
#Controller
#RequestMapping("/registerMember")
public class RegisterMemberController {
#Autowired
private MemberService memberService;
#GetMapping
public String index(RegisterMemberBean registerMemberBean) {
return "registerMember";
}
#PostMapping
public String handleSubmit(#Valid RegisterMemberBean registerMemberBean, BindingResult bindingResult, Model model) {
Member member = registerMemberBean.getMember();
boolean isRepeatPasswordCorrect = !isRepeatPasswordIncorrect(member.getPassword(), registerMemberBean.getComparePassword());
if(isAnyErrors(isRepeatPasswordCorrect, !bindingResult.hasErrors())) {
if(!isRepeatPasswordCorrect) {
model.addAttribute("isRepeatPasswordIncorrect", true).
addAttribute("isRepeatPasswordIncorrectMsg", "Passwords don't match");
}
return "registerMember";
}
boolean errUsername = !memberService.isNoOtherEntityWithUserName(0, member.getUserName());
boolean errEmail = !memberService.isNoOtherEntityWithEmail(0, member.getEmail());
if(errUsername || errEmail) {
if(errUsername) {
model.addAttribute("isExistingUserName", true).addAttribute("isExistingUserNameMsg", "Already a user with that username");
} if(errEmail) {
model.addAttribute("isExistingEmail", true).addAttribute("isExistingEmailMsg", "Already a user with that email");
}
return "registerMember";
}
getMainService().save(member);
return redirectTo("index", new RedirectEntity("member", member.getId()));
}
}
Now in my mock controller test i want to make make sure that my post method does the following:
Reload the page if the BindingResults has any errors
My service persists the member JPA entity in db (if no errors)
Method redirects me to the index page
This is what my (poor) test class looks like so far:
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:application_test.properties")
#WebAppConfiguration
public class RegisterMemberControllerTest {
private MockMvc mockMvc;
#MockBean
private MemberService memberService;
#MockBean
private RegisterMemberController controller;
#Before
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new StandaloneMvcTestViewResolver()).build();
controller.setMainService(memberService);
}
#Test
public void testIndex() throws Exception {
mockMvc.perform(get("/registerMember"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("registerMember");
}
#Test
public void testHandleSubmit() throws Exception {
RegisterMemberBean registerMemberBean = new RegisterMemberBean();
registerMemberBean.setMember(TestFixture.getValidMemberWithoutReferences());
Member member = TestFixture.getValidMember();
mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect(status().isOk());
when(mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect((ResultMatcher) memberService.save(member)).andExpect(forwardedUrl("redirect:/index/member=" + member.getId() + ".html")));
}
}
to my understanding spring boot uses Mockito. I have some experience with EasyMock but I would like to use the spring defaults as much as possible. Can someone show how to achieve this?
I think there is a little bit of confusion on what should and shouldn't be mocked.
If I read your question correctly, you are actually trying to Unit Test your RegisterMemberController. Therefore, you most likely should NOT make a mock of that class, but actually test that class.
I believe that you would be creating fakes/dummies/stubs/mocks/spies of your MemberService, RegisterMemberBean, and BindingResult classes.
It would be these classes that would be created by your unit test and handed to your controller during the test that will force the testing of the logic that you are interested in proving/disproving.
FYI, when verifying that the MemberService class was called, that is where you would use a mock. The rest of the classes could either be dummies or stubs.
Side Note: I would recommend removing the Model parameter from your handleSubmit() method since it doesn't seem to be used anywhere.
This is my Mockito Test:
#RunWith(MockitoJUnitRunner.class)
public class Controller_Test {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Mock
private InputValidationService inputValidationService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(restController).build();
}
#Test
public void testGetEmployeeDetails() {
EmployeeController spy = Mockito.spy(employeeController);
MvcResult result = mockMvc.perform(get("/employee/details/9816")).andDo(print()).andExpect(status().isOk()).andReturn();
// Have some basic asserts here on the result that are working fine
}
}
Now my question is, Is there a way to assert that the method I expected to be called in my controller was actually invoked.
I know it was, but how do I assert it with unit test
For e.g.
This is my RequestMapping in the controller:
#RequestMapping(value = "/employee/details/{id}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public EmployeeDetails getEmployeeDetailsById(#PathVariable String employeeID) {
//Some business logic
}
Now I would like to make some assertion like:
Mockito.verify(spy, times(1)).getEmployeeDetailsById();
So basically I would like to assert that the method I expected to get called was the one that got called. I know this can be done on the Mock Service object that I have i.e. inputValidationService but would like something similar for the controller as well.
Please let me know if there are any additional details that you would like me to post.
Maybe late, but I found org.springframework.test.web.servlet.result.HandlerResultMatchers which can verify the proper controller and method are being called. For example:
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler;
mockMvc
.perform(get("/employee/details/9816"))
.andExpect(handler().handlerType(EmployeeController.class))
.andExpect(handler().methodName("getEmployeeDetailsById"));
You can use #MockBean.This will use Mockito beans for which you will be able to use standart Mockito functions like "verify".
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
In my integration test, I tried to use resttemplate to send a Get request to a dummy server created by MockMvcBuilders. However I got an error:
I/O error on GET request for "http://localhost:8080/test":Connection refused:
(In the function testAccess(), url is "http://localhost:8080/test"). My code is as below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#IntegrationTest("server.port=8080")
public class MyTest {
private MockMvc mockMvc = null;
#Autowired
private WebApplicationContext context;
#Value("${server.port}")
private int port;
#Autowired
private MyController myController;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
#Test
public void testAccess() throws Exception{
RestTemplate restTemplate=new RestTemplate();
String url="http://localhost:8080/test";
try{
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
}
catch(ResourceAccessException ex){
System.out.println(ex.getMessage());
}
}
#Controller
public static class MyController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String access() {
return "OK";
}
}
}
The way I've done it is this:
First, you create a mock server from the actual RestTemplate you are using in your application
#Before
public void setup() throws Exception {
mockServer = MockRestServiceServer.createServer(myService.restTemplate);
}
Then you define how that request is going to work:
mockServer.expect(requestTo("http://localhost:8080/myrestapi"))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{ success: true }", MediaType.APPLICATION_JSON));
And last you call the method in your application that will trigger a call to that url with that RestTemplate:
#Test
public void testThis() throws Exception {
myService.somethingThatCallsMyRestApi(parameters);
}
That will make your tests work as if there was a server up and running to process requests.
Using this in your example makes no sense, cause you would be testing that you build your test correctly and nothing else from the actual application.
The problem with this is that you cannot test dynamic responses. I mean, in my case the method I'm calling generates different data every time you call it and then sends it to the mockServer and then validates that the response matches in some very specific way. I haven't found a solution yet, but if the data you are going to send and receive is previously known, which would be in most cases, you'll have no problem using this.
Why are you defining a controller in your Test class and then trying to test it ? It doesn't feel logical to try to test something that is defined within the test it self.
Rather you would want to test a controller defined somewhere outside your tests, an actual controller that is used within your application.
Let's say MyController is defined as an actual controller then you could use the mockMvc object you created to test it.
mockMvc.perform(get('/test'))
.andExpect(status().isOk())