测试
我想大家对于测试都不陌生,常见的测试,有各种不同的分类,比如单元测试、集成测试、系统测试等等。 一般由程序员自己做的测试有单元测试与系统测试。 单元测试,不同的人对于单元的含义有不同的理解,有的人认为只有函数是一个单元,有的人认为一个类是一个单元, 不过大家都认为单元测试都应该很快,而如果要达成这种效果,就应该避免操作数据库,访问文件系统或者法送真实的网络请求, 所以一般情况下应该 mock 测试中的依赖。
spring 单元测试
我们使用 spring 进行单元测试时,可以只注入需要测试的类,将其他需要测试的类使用 mock 实现。 如果我们要对如下的代码进行测试
@Service
public class OrderService {
@Autowired
private OrdersRepository ordersRepository;
public Long placeOrder(Order order) {
return ordersRepository.createOrder(order);
}
}
复制代码
测试代码可以如下
@RunWith(SpringRunner.class)
@SpringBootTest
class 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);
}
}
复制代码
返回的类型是
@Data
public class ApiResult {
private Integer code;
private String message;
private Object data;
}
复制代码
基于 standaloneSetup
@RunWith(SpringRunner.class)
@SpringBootTest
class 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)
@SpringBootTest
class 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
评论