I have a hypothetical rest end point.
@GetMapping(value = "/happy/{happyId}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Response<?> getHappy(@PathVariable Long happyId) {
Response response = new Response();
response.update(happyService.getById(happyId));
return response;
}
In this code, happyService could throw UnhappyException if id does not exist, and this code is tested in another place, eg) HappyServiceTest.
Now, let say if I want to test my rest controller, should I also be testing the exception flow? Or is this unnecessary?
eg)
HappyRestControlerTest.java
@Test
void testUnHappy() {
...
assertThrows(UnhappyException.class () -> {happyService.getById(-1L)});
}
Is this unnecessary test since I tested the behaviour of happyService in HappyServiceTest?
CodePudding user response:
Considering your example is a unit test, the real HappyService
should not even be in this equation and should be mocked. So if you were writing a test for the controller facade, you'd essentially be testing that it
A. Calls HappyService
, given ID X, Y is returned
B. Calls HappyService
, given ID X, exception is thrown and propagated.
However a unit test is quite pointless for controllers whose only job is to forward a call to a service. There is a better way to test controllers where a web server is brought up and the test is performed against your Spring context to the extent that you choose, allowing you to test the controller by making HTTP requests to it and checking its output that way. This approach is outlined in Testing the Web Layer.
CodePudding user response:
For this layer of application there is a specific type of testing. It is called MVC testing and for this you mock your service with some specific response for some specific input and you verify with a test that the Controller behaves as expected.
See the following example
@SpringBootTest
@AutoConfigureMockMvc
public class TestingWebApplicationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private HappyService service;
@Test
public void shouldReturnMessage() throws Exception {
when(service.getById(1)).thenReturn("Happy Response 1");
this.mockMvc.perform(get("/happy/1"))
.andExpect(status().isOk())
.andExpect(content()
.string(containsString("Happy Response 1")));
}
}
This is a type of test that simulates, what the client will receive from controller http status code, http headers, http body content etc...
Spring Boot already includes support for this MockMvc testing via the dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
CodePudding user response:
There are different types of testing. You are talking about unit testing. In Unit testing you need to strive to break the code to the smallest logical blocks (units) and test them each with a separate unit test. As you mentioned, you already tested internal logic of your controller elsewhere. And that is correct. Testing a controller itself in unit testing doesn't make much sense at all. So, the answer to your question - no controller itself should not be tested in Unit Testing. There other types of tests such as load (stress) test, Functional tests etc. Controller should be tested in Functional testing where you test the entire logical path rather then the smallest logical unit. I won't get into details how it is done here, but controller basic functionality test is part of functional test and not unit test.