这种Controller层测试方案会(部分)加载Spring Application Context。不过仍然还是主要是用MockMVC来进行测试,也不需要部署WebServer。
示例代码如下:
@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 (部分)完成了Spring Context的初始化工作,在执行测试的时候,在日志开始的部分可以看到加载Context的内容。
使用 @ WebMVCTest 注解可以完成 MockMVC 实例的自动化配置,以便于使用 @ Autowire 注解来引用这个实例。同时,在 @ WebMVCTest 注解中还指明了要测试的Controller类,这样Spring就会在Context中加载该Controller类及其相关的配置项。
此外, @ WebMVCTest 注解还会主动发现Controller类相关的Filter类和Controller Advice类并完成注入。这样,我们就不需要在 setup ( ) 方法中再对其进行配置了。
前面提到过这种测试方案是 部分 加载了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的测试,如果有更多的其它角色,也可以直接集成到测试中。
其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824