转载

Spring Controller层测试 – 03 WebContext & MockMVC

这种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 来执行的。 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。参与测试的角色如下图:

Spring Controller层测试 – 03 WebContext &amp; MockMVC

如果我们创建了新的Filter、新的Controller Advice或者其他参与到WEB请求响应过程中的角色,也都会在测试中完成自动注入,不需要任何其它的配置。这和我们实际使用的场景非常类似。

这次测试可以算是向集成测试的一个小小过渡。这里我们没有做任何配置就实现了对Filter和Controller Advice的测试,如果有更多的其它角色,也可以直接集成到测试中。

其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824

原文  http://www.zhyea.com/2019/03/29/spring-controller-test-03-webcontext-mockmvc.html
正文到此结束
Loading...