测试
我想大家对于测试都不陌生,常见的测试,有各种不同的分类,比如单元测试、集成测试、系统测试等等。 一般由程序员自己做的测试有单元测试与系统测试。 单元测试,不同的人对于单元的含义有不同的理解,有的人认为只有函数是一个单元,有的人认为一个类是一个单元, 不过大家都认为单元测试都应该很快,而如果要达成这种效果,就应该避免操作数据库,访问文件系统或者法送真实的网络请求, 所以一般情况下应该 mock 测试中的依赖。
spring 单元测试
我们使用 spring 进行单元测试时,可以只注入需要测试的类,将其他需要测试的类使用 mock 实现。 如果我们要对如下的代码进行测试
 @Servicepublic class OrderService {
    @Autowired    private OrdersRepository ordersRepository;
    public Long placeOrder(Order order) {        return ordersRepository.createOrder(order);    }}
   复制代码
 
测试代码可以如下
 @RunWith(SpringRunner.class)@SpringBootTestclass OrderServiceTest {
    @Autowired    private OrderAppService orderAppService;
    @MockBean    private OrdersRepository ordersRepository;
    @BeforeEach    void setUp() throws Exception {    }
    @Test    void testPlaceOrder() {        Order order = new Order();        //mock ordersRepository ,让所有的保存都返回2        Mockito.when(ordersRepository.createOrder(ArgumentMatchers.any(Order.class)))                .thenReturn(2L);        long result = orderAppService.placeOrder(order);        assertEquals(result, 2L);    }
}
   复制代码
 
@MockBean 声明这是一个模拟的 bean。在进行单元测试时,需要将测试目标的所有依赖 bean 声明为模拟的 bean,这些模拟的 bean 将被注入测试目标 bean。 使用 Mockito.when 对 ordersRepository.createOrder()方法进行打桩,设定当方法被调用时,直接返回我们预设的数据。
测试 web 应用程序
如果要测试 controller 等 web 组建,一种方式是直接将程序发布到容器中进行测试,另一种方式是使用 mock 模拟测试 在 spring 中可以通过 Mock MVC,在一个近似真实的模拟 Servlet 容器里测试控制器。
要在测试中设置 Mock MVC,可以用 MockMvcBuilders,它提供了两个静态方法可以是实现:
- standaloneSetup():构建一个 Mock MVC,提供一个或多个手工初始化并注入我们要测试的控制器(需要手工初始化并注入要测试的控制器) 
- webAppContextSetup():使用 Spring 应用程序的上下文来构建 Mock MVC,该上下文可以包含一个或多个配置好的控制器(基于 WebApplicationContext 的实例,由 Spring 加载控制器和其依赖) 
准备测试代码
以下是我们要测试的代码
 @RestController@RequestMapping(value = "orders")public class OrdersController {
    @Autowired    private OrderAppService orderService;
    @PostMapping("createOrder")    public ApiResult createOrder(OrderDto orderDto) {        Order order = OrderDto.toOrder(orderDto);        Long orderId = orderService.placeOrder(order);        return ApiResult.success(orderId);    }}
   复制代码
 
返回的类型是
 @Datapublic class ApiResult {
    private Integer code;    private String message;    private Object data;}
   复制代码
 
基于 standaloneSetup
 @RunWith(SpringRunner.class)@SpringBootTestclass OrdersControllerStTest {
    @MockBean    private OrderAppService orderAppService;
    @InjectMocks    private OrdersController ordersController;
    private MockMvc mockMvc;
    @BeforeEach    void setUp() throws Exception {        MockitoAnnotations.initMocks(this);//必须初始化,否则无法注入 OrderAppService        mockMvc = MockMvcBuilders.standaloneSetup(ordersController).build();        Mockito.when(orderAppService.placeOrder(ArgumentMatchers.any(Order.class))).thenReturn(2L);    }
    @Test    void test() throws Exception {        mockMvc.perform(post("/orders/createOrder") //  发起post请求                .contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk()) //希望请求处理成功(isOk()会判断HTTP 200响应码)                .andExpect(MockMvcResultMatchers.jsonPath("$.code", is(0)))                .andExpect(MockMvcResultMatchers.jsonPath("$.data", is(2)))                .andExpect(MockMvcResultMatchers.jsonPath("$.message", is("success")));    }
}
   复制代码
 
- @MockBean 创建模拟。这也可以通过使用 org.mockito.mock 方法实现。 
- @InjectMocks 自动将模拟或间谍字段注入测试对象。 
- MockitoAnnotations.initMocks(this)初始化了使用 Mockito 注释的字段。 
- MockMvcBuilders.standaloneSetup(…).build() 通过注册一个或多个实例和以编程方式配置 spring MVC @Controller 基础结构来构建 MockMvc 实例。 
webAppContextSetup
 @RunWith(SpringRunner.class)@SpringBootTestclass OrdersControllerTest {
     @Autowired    private WebApplicationContext wac;//注入WebApplicationContext
    @MockBean//可以将 mock 对象注入到 WebApplicationContext中    private OrderAppService orderAppService;
    private MockMvc mockMvc;
    @BeforeEach    void setUp() throws Exception {        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();        Mockito.when(orderAppService.placeOrder(ArgumentMatchers.any(Order.class))).thenReturn(2L);    }
    @Test    void test() throws Exception {        mockMvc.perform(post("/orders/createOrder") //  发起post请求                .contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk()) //希望请求处理成功(isOk()会判断HTTP 200响应码)                .andExpect(MockMvcResultMatchers.jsonPath("$.code", is(0)))                .andExpect(MockMvcResultMatchers.jsonPath("$.data", is(2)))                .andExpect(MockMvcResultMatchers.jsonPath("$.message", is("success")));    }
}
   复制代码
 
- 与单独测试不同,应为使用了 WebApplicationContext ,所以在获取 Controller 的时候,就可以对 Service 进行 Mock 
Mvc 测试的一般流程
- 准备测试环境 
- 通过 MockMvc 执行请求 
- 添加验证断言 
- 添加结果处理器 
- 得到 MvcResult 进行自定义断言/进行下一步的异步请求 
- 卸载测试环境 
参考资料
- 3.7. MockMvc
 
 
评论