Testing: mocking
This commit is contained in:
parent
45d0361548
commit
0ddd30a245
88
README.md
88
README.md
@ -269,4 +269,90 @@ public class WebLayerTest {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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)`.
|
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)`.
|
||||||
|
|
||||||
|
## Testing with mocks
|
||||||
|
|
||||||
|
So far, our `GreetingController` is simple and has no dependencies. We could make it more realistic by introducing an extra component, say another service which will harbor some business logic, in our case - calculate length of a URL `name` parameter.
|
||||||
|
|
||||||
|
`src/main/java/djmil/hellomvc/GreetingService.java`
|
||||||
|
```java
|
||||||
|
package djmil.hellomvc;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GreetingService {
|
||||||
|
|
||||||
|
public int nameLength(String name) {
|
||||||
|
return name.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In SpringBoot [`@Service`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Service.html) annotation is usually used to mark business logic classes. Doing so allows SpringBoot IoC to handle dependencies between different classes. Here, we have to update `GreetingsControler` to use `GrretingsSrvice` to handle `/greeting` request:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Controller
|
||||||
|
public class GreetingController {
|
||||||
|
|
||||||
|
private final GreetingService greetigService;
|
||||||
|
|
||||||
|
public GreetingController(GreetingService greetigService) {
|
||||||
|
this.greetigService = greetigService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/greeting")
|
||||||
|
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
|
||||||
|
|
||||||
|
model.addAttribute("name", name);
|
||||||
|
model.addAttribute("nameLength", greetigService.nameLength(name));
|
||||||
|
// NOTE: Here we can request some data from our RESTful backend as well
|
||||||
|
|
||||||
|
return "greeting"; // view tempalte
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Spring IoC will insert instance of `GreetingService` as a constructor parameter of `GreetingController`, which we store as a private class member for later use.
|
||||||
|
|
||||||
|
Also, to make full use of newly created service, update `src\main\resources\templates\greeting.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<p th:text="'Hello, ' + ${name} + '!'" />
|
||||||
|
<p th:text="'Length of a given name is ' + ${nameLength} + ' characters.'" />
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the tests, and notice that only `GreetingControllerTest` has failed.
|
||||||
|
|
||||||
|
### Mocking
|
||||||
|
|
||||||
|
To fix the test, we have to provide mock implementation of a `GreetingService` class inside `GreetingControllerTest`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@WebMvcTest(GreetingController.class)
|
||||||
|
public class CreetingControllerTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private GreetingService service;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void greetingShouldReturnMessageFromService() throws Exception {
|
||||||
|
when(service.nameLength("World")).thenReturn(7);
|
||||||
|
this.mockMvc.perform(get("/greeting"))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(containsString("Length of a given name is 7 characters.")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice the `@MockBean` annotation, which is essentially used to obtain an interface of a mocked class. Later, `when` function used to define an actual mock.
|
@ -8,13 +8,20 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
@Controller
|
@Controller
|
||||||
public class GreetingController {
|
public class GreetingController {
|
||||||
|
|
||||||
|
private final GreetingService greetigService;
|
||||||
|
|
||||||
|
public GreetingController(GreetingService greetigService) {
|
||||||
|
this.greetigService = greetigService;
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/greeting")
|
@GetMapping("/greeting")
|
||||||
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
|
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
|
||||||
|
|
||||||
// NOTE: Here we can request some data from our RESTful backend as well
|
|
||||||
model.addAttribute("name", name);
|
model.addAttribute("name", name);
|
||||||
|
model.addAttribute("nameLength", greetigService.nameLength(name));
|
||||||
|
// NOTE: Here we can request some data from our RESTful backend as well
|
||||||
|
|
||||||
return "greeting"; // <<-- template
|
return "greeting"; // view tempalte
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
12
src/main/java/djmil/hellomvc/GreetingService.java
Normal file
12
src/main/java/djmil/hellomvc/GreetingService.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package djmil.hellomvc;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GreetingService {
|
||||||
|
|
||||||
|
public int nameLength(String name) {
|
||||||
|
return name.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p th:text="'Hello, ' + ${name} + '!'" />
|
<p th:text="'Hello, ' + ${name} + '!'" />
|
||||||
|
<p th:text="'Length of a given name is ' + ${nameLength} + ' characters.'" />
|
||||||
<input placeholder="Enter username.."/>
|
<input placeholder="Enter username.."/>
|
||||||
<button>Go!</button>
|
<button>Go!</button>
|
||||||
</body>
|
</body>
|
||||||
|
41
src/test/java/djmil/hellomvc/CreetingControllerTests.java
Normal file
41
src/test/java/djmil/hellomvc/CreetingControllerTests.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package djmil.hellomvc;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
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.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
@WebMvcTest(GreetingController.class)
|
||||||
|
public class CreetingControllerTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private GreetingService service;
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void shouldReturnDefaultMessage() throws Exception {
|
||||||
|
// this.mockMvc.perform(get("/greeting"))
|
||||||
|
// .andDo(print())
|
||||||
|
// .andExpect(status().isOk())
|
||||||
|
// .andExpect(content().string(containsString("Hello, World")));
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void greetingShouldReturnMessageFromService() throws Exception {
|
||||||
|
when(service.nameLength("World")).thenReturn(7);
|
||||||
|
this.mockMvc.perform(get("/greeting"))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().string(containsString("Length of a given name is 7 characters.")));
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
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")));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user