这种Controller层测试方案会(部分)加载Spring Application Context。不过仍然还是主要是用MockMVC来进行测试,也不需要部署WebServer。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
@RunWith(SpringRunner.class) @WebMvcTest(WorkerController.class) public class WorkerControllerMockMvcWithContextTest { @Autowired private MockMvc mockMvc; @MockBean private IWorkerService workerService; private JacksonTester<Worker> jsonWorker; @Before public void setup() { JacksonTester.initFields(this, new ObjectMapper()); System.out.println(); } @Test public void getWhenExists() throws Exception { //given given(workerService.get(2)).willReturn(new Worker("LiLei", 16)); //when MockHttpServletResponse response = mockMvc.perform(get("/worker/2").accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); //then assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getContentAsString()).isEqualTo(jsonWorker.write(new Worker("LiLei", 16)).getJson()); } @Test public void getWhenNotExists() throws Exception { //given given(workerService.get(2)).willThrow(new NonExistingWorkerException()); //when MockHttpServletResponse response = mockMvc.perform(get("/worker/2").accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); //then assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value()); assertThat(response.getContentAsString()).isEmpty(); } @Test public void getByNameWhenExists() throws Exception { //given given(workerService.getByName("LiLei")).willReturn(Optional.of(new Worker("LiLei", 16))); //when MockHttpServletResponse response = mockMvc.perform(get("/worker?name=LiLei").accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); //then assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getContentAsString()).isEqualTo(jsonWorker.write(new Worker("LiLei", 16)).getJson()); } @Test public void getByNameWhenNotExists() throws Exception { //given given(workerService.getByName("LiLei")).willReturn(Optional.empty()); //when MockHttpServletResponse response = mockMvc.perform(get("/worker?name=LiLei").accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); //then assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getContentAsString()).isEmpty(); } @Test public void add() throws Exception { MockHttpServletResponse response = mockMvc.perform( post("/worker").contentType(MediaType.APPLICATION_JSON) .content(jsonWorker.write(new Worker("Jerry", 12)).getJson()) ).andReturn().getResponse(); assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); } @Test public void workerFilter() throws Exception { //when MockHttpServletResponse response = mockMvc.perform(get("/worker/2").accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); //then assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getHeaders("X-CHOBIT-APP")).containsOnly("chobit-header"); } } |
和standalone MockMVC模式相比,这种测试方案主要有如下几点不同。
SpringRunner
这种测试是由SpringRunner
来执行的。SpringRunner
(部分)完成了Spring Context的初始化工作,在执行测试的时候,在日志开始的部分可以看到加载Context的内容。
MockMVC 自动化配置
使用@WebMVCTest
注解可以完成MockMVC
实例的自动化配置,以便于使用@Autowire
注解来引用这个实例。同时,在@WebMVCTest
注解中还指明了要测试的Controller类,这样Spring就会在Context中加载该Controller类及其相关的配置项。
此外,@WebMVCTest
注解还会主动发现Controller类相关的Filter类和Controller Advice类并完成注入。这样,我们就不需要在setup()
方法中再对其进行配置了。
使用MockBean
前面提到过这种测试方案是部分加载了Spring Context。原因就是@WebMVCTest
在加载类的时候只会扫描含有@Controller
,@ControllerAdvice
, @JsonComponent
,Filter
,WebMvcConfigurer
和HandlerMethodArgumentResolver
这些注解或接口的类,也就是在三层模型中WEB层相关的类,而@Component
注解的类则会被忽略掉。因此我们无法直接从Context获取IWorkerService
实例。
因此测试中使用的WorkerService实例是通过@MockBean
注解生成并注入到了Spring Context中的,并不是真正的WorkerService实例。
没有真正的服务调用
需要注意这里的返回值仍然是伪造的,在测试中也没有用到任何Web Server。
这次测试的主要关注点仍然是Controller类的内部逻辑,以及一些相关的角色如Filter和Controller Advice是如何影响Controller的返回值的。
总结
这次测试和使用MockMVC Standalone模式的主要区别在于不需要显式加载Controller相关的角色,因为这里使用到了Spring Context。参与测试的角色如下图:
如果我们创建了新的Filter、新的Controller Advice或者其他参与到WEB请求响应过程中的角色,也都会在测试中完成自动注入,不需要任何其它的配置。这和我们实际使用的场景非常类似。
这次测试可以算是向集成测试的一个小小过渡。这里我们没有做任何配置就实现了对Filter和Controller Advice的测试,如果有更多的其它角色,也可以直接集成到测试中。
- Spring Controller测试 – 01 概述
- Spring Controller测试 – 02 Standalone MockMVC
- Spring Controller测试 – 03 WebContext & MockMVC
- Spring Controller测试 – 04 SpringBootTest & MockMVC
- Spring Controller测试 – 05 SpringBootTest & WebServer
其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824
发表评论