How to make a simple rest api controller test?

Viewed 289

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.

3 Answers

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