该文章已同步至个人微信公众号[不能止步],欢迎关注。
1. 场景
在单元测试场景中,一种典型的场景是为了测试某一个类(Component Under Test, 简称CUT)而需要mock其依赖的的类。示例如下:
// 被测组件publicClassOrder{privatefinalOrderRepository orderRepository;publicvoidcreate(CreateOrderDTO createOrder)validateCreateOrDer(createOrder);var order =processOrder(createOrder);
orderRepository.save(order);}
为了验证CUT业务实现的正确性,通常需要验证传给调用Mock对象的方法的参数的正确性。如果采用Mockito测试框架,一般可以按如下方式实现测试用例:
publicClassOrderServiceTest{privatefinalOrderRepository orderRepository =mock(OrderRepository.class);privatefinalOrderService orderService =newOrderService(orderRepsitory);publicvoidshouldSuccessWhenCreateOrder(){when(orderRepository.save(any).thenReturn(any());CreateOrderDTO createOrder =...
orderService.create(createOrder);// 验证参数是否与期望的一致verify(orderRepository,times(1)).save(argThat(order ->
order.userId().equals(createOrder.userId())&&
order.price()==100));}}
2. 问题
采用上述方法(argThat)测试Mock对象的方法的入参的方式虽然可以验证入参是否符合期望,但细看代码,我们依然会发现几个问题。
- 测试代码不美观,如果待验证的参数比较多,要用很多个&&连接;
- 测试用例失败时无法给出是哪一个参数不正确,如上例中,在argThat中的lambda function表达式返回false时,测试用例失败的信息无法让开发者直接知道是userId还是price不符合期望,开发者还需要进行代码调试以确认到底是哪个参数不符合期望。
Argument(s) are different! Wanted:
orderRepository.save(
<custom argument matcher>
);
Actual invocations have different arguments:
orderRepository.save(
captor.demo.persistence.entity.Order@441aa7ae
);
3. 如何优雅的验证Mock对象的方法的入参
在Mockito测试框架下,Mockit提供了对Mock对象方法的入参捕获,其做法是:
- 用MockitoExtension.class作为测试的执行引擎;
- 使用@Captor注解定义一个参数捕获对象,并在CUT调用的被Mock对象的方法中传入定义的参数捕获对象;
- 从定义的参数捕获对象中获得捕获的参数,使用assert进行验证。
采用该方法对上述示例的改进如下:
// 1. 使用MockitoExtension测试执行引擎@ExtendWith(MockitoExtension.class)publicClassOrderServiceTest{privatefinalOrderRepository orderRepository =mock(OrderRepository.class);privatefinalOrderService orderService =newOrderService(orderRepsitory);//2. 使用@Captor注解定义一个参数捕获对象,泛型类型为待捕获的参数类型@CaptorprivateArgumentCaptor<Order> orderCaptor;publicvoidshouldSuccessWhenCreateOrder(){when(orderRepository.save(any).thenReturn(any());CreateOrderDTO createOrder =...
orderService.create(createOrder);//3. 被Mock对象的方法中传入定义的参数捕获对象,注意需要调用参数捕获对象的capture方法verify(orderRepository,times(1)).save(orderCaptor.capture());//4. 从定义的参数捕获对象获得捕获的入参,使用assert进行验证var order = orderCaptor.getValue();assertThat(order.userId()).isEqualTo(createOrder.userId());assertThat(order.price()).isEuqalTo(100);}}
对比一下argThat和@Captor两种测试Mock对象的方法参数的方法,使用@Captor这种方法具有:
- 代码美观,修改容易;
- 测试用例失败时能够快速定位出哪个参数不符合期望。
org.opentest4j.AssertionFailedError:
Expected :100
Actual :50
版权归原作者 不能止步 所有, 如有侵权,请联系我们删除。