From 45d03615487193f9dc4fc22716cccd012dac20ac Mon Sep 17 00:00:00 2001 From: djmil Date: Wed, 26 Jul 2023 14:39:15 +0200 Subject: [PATCH] Testing: controller --- README.md | 35 ++++++++++++++++--- .../java/djmil/hellomvc/WebLayerTest.java | 27 ++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/test/java/djmil/hellomvc/WebLayerTest.java diff --git a/README.md b/README.md index 8f1e8d9..75620d0 100644 --- a/README.md +++ b/README.md @@ -169,9 +169,15 @@ Spring interprets the `@Autowired` annotation, and the controller is injected be > A nice feature of the Spring Test support is that the application context is cached between tests. That way, if you have multiple methods in a test case or multiple test cases with the same configuration, they incur the cost of starting the application only once. You can control the cache by using the @DirtiesContext annotation. -## Test HTTP requests +It is nice to have a sanity check, but you should also write some tests that assert the behavior of your application. -It is nice to have a sanity check, but you should also write some tests that assert the behavior of your application. To do that, you could start the application and listen for a connection (as it would do in production) and then send an HTTP request and assert the response. The following listing (from `src/test/java/djmil/hellomvc/HttpRequestTest.java`) shows how to do so: +## Testing depth + +You can as SpringBoot to test your app at the different depth. Trading test coverage for speed. + +### HTTP requests: application + +Here, we are starting our application and listen for a connection (as it would do in production) and then send an HTTP request and assert the response. The following listing (from `src/test/java/djmil/hellomvc/HttpRequestTest.java`) shows how to do so: ```java package djmil.hellomvc; @@ -205,7 +211,7 @@ public class HttpRequestTest { Note the use of `webEnvironment=RANDOM_PORT` to start the server with a random port (useful to avoid conflicts in test environments) and the injection of the port with `@LocalServerPort`. Also, note that Spring Boot has automatically provided a `TestRestTemplate` for you. All you have to do is add `@Autowired` to it. -## Test Web app only +### Web app only Another useful approach is **to not start the server at all** but to test only the layer below that, where Spring handles the incoming HTTP request and hands it off to your controller. That way, almost of the full stack is used, and your code will be called in exactly the same way as if it were processing a real HTTP request but without the cost of starting the server. To do that, use Spring’s `MockMvc` and ask for that to be injected for you by using the `@AutoConfigureMockMvc` annotation on the test case. The following listing (from `src/test/java/djmil/hellomvc/WebApplicationTest.java`) shows how to do so: @@ -242,4 +248,25 @@ public class WebApplicationTest { } ``` -In this test, the full Spring application context is started but without the server. \ No newline at end of file +In this test, the full Spring application context is started but without the server. + +### Controller: web layer only + +We can narrow the tests to only the web layer by using `@WebMvcTest`, as the following listing (from `src/test/java/djmil/hellomvc/WebLayerTest.java`) shows: + +```java +@WebMvcTest +public class WebLayerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void shouldReturnDefaultMessage() throws Exception { + this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk()) + .andExpect(content().string(containsString("Hello, World"))); + } +} +``` + +The test assertion is the same as in the previous case. However, in this test, Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated by using, for example, `@WebMvcTest(HomeController.class)`. \ No newline at end of file diff --git a/src/test/java/djmil/hellomvc/WebLayerTest.java b/src/test/java/djmil/hellomvc/WebLayerTest.java new file mode 100644 index 0000000..cce88bc --- /dev/null +++ b/src/test/java/djmil/hellomvc/WebLayerTest.java @@ -0,0 +1,27 @@ +package djmil.hellomvc; + +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.web.servlet.MockMvc; + +@WebMvcTest +public class WebLayerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void shouldReturnDefaultMessage() throws Exception { + this.mockMvc.perform(get("/greeting")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Hello, World"))); + } +} \ No newline at end of file