51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 192|回复: 0
打印 上一主题 下一主题

[转贴] Mockito框架下如何优雅的验证调用Mock对象方法的入参

[复制链接]
  • TA的每日心情
    无聊
    2024-3-1 09:27
  • 签到天数: 43 天

    连续签到: 2 天

    [LV.5]测试团长

    跳转到指定楼层
    1#
    发表于 2024-2-29 11:27:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    1. 场景

    在单元测试场景中,一种典型的场景是为了测试某一个类(Component Under Test, 简称CUT)而需要mock其依赖的的类。示例如下:

    1. <font size="3">// 被测组件
    2. public Class Order {
    3.     private final OrderRepository orderRepository;
    4.    
    5.     public void create(CreateOrderDTO createOrder)
    6.         validateCreateOrDer(createOrder);

    7.         var order = processOrder(createOrder);

    8.          orderRepository.save(order);
    9. }

    10. </font>
    复制代码

    为了验证CUT业务实现的正确性,通常需要验证传给调用Mock对象的方法的参数的正确性。如果采用Mockito测试框架,一般可以按如下方式实现测试用例:

    1. <font size="3">public Class OrderServiceTest {
    2.     private final OrderRepository orderRepository = mock(OrderRepository.class);
    3.     private final OrderService orderService = new OrderService(orderRepsitory);
    4.    
    5.     public void shouldSuccessWhenCreateOrder() {
    6.          when(orderRepository.save(any).thenReturn(any());
    7.          CreateOrderDTO createOrder = ...
    8.          
    9.          orderService.create(createOrder);
    10.    
    11.           // 验证参数是否与期望的一致
    12.           verify(orderRepository, times(1)).save(argThat(order ->
    13.                 order.userId().equals(createOrder.userId()) &&
    14.                 order.price() == 100));
    15.     }
    16. }   
    17. </font>
    复制代码

    2. 问题

    采用上述方法(argThat)测试Mock对象的方法的入参的方式虽然可以验证入参是否符合期望,但细看代码,我们依然会发现几个问题。


    测试代码不美观,如果待验证的参数比较多,要用很多个&&连接;

    测试用例失败时无法给出是哪一个参数不正确,如上例中,在argThat中的lambda function表达式返回false时,测试用例失败的信息无法让开发者直接知道是userId还是price不符合期望,开发者还需要进行代码调试以确认到底是哪个参数不符合期望。

    1. <font size="3">Argument(s) are different! Wanted:
    2. orderRepository.save(
    3.     <custom argument matcher>
    4. );

    5. Actual invocations have different arguments:
    6. orderRepository.save(
    7.    captor.demo.persistence.entity.Order@441aa7ae
    8. );
    9. </font>
    复制代码

    3. 如何优雅的验证Mock对象的方法的入参

    在Mockito测试框架下,Mockit提供了对Mock对象方法的入参捕获,其做法是:


    用MockitoExtension.class作为测试的执行引擎;

    使用@Captor注解定义一个参数捕获对象,并在CUT调用的被Mock对象的方法中传入定义的参数捕获对象;

    从定义的参数捕获对象中获得捕获的参数,使用assert进行验证。

    采用该方法对上述示例的改进如下:

    1. <font size="3">// 1. 使用MockitoExtension测试执行引擎
    2. @ExtendWith(MockitoExtension.class)
    3. public Class OrderServiceTest {
    4.     private final OrderRepository orderRepository = mock(OrderRepository.class);
    5.     private final OrderService orderService = new OrderService(orderRepsitory);
    6.    
    7.     //2. 使用@Captor注解定义一个参数捕获对象,泛型类型为待捕获的参数类型
    8.     @Captor
    9.     private ArgumentCaptor<Order> orderCaptor;
    10.    
    11.     public void shouldSuccessWhenCreateOrder() {
    12.          when(orderRepository.save(any).thenReturn(any());
    13.          CreateOrderDTO createOrder = ...
    14.          
    15.          orderService.create(createOrder);
    16.    
    17.           //3. 被Mock对象的方法中传入定义的参数捕获对象,注意需要调用参数捕获对象的capture方法
    18.           verify(orderRepository, times(1)).save(orderCaptor.capture());
    19.           //4. 从定义的参数捕获对象获得捕获的入参,使用assert进行验证
    20.           var order = orderCaptor.getValue();
    21.           assertThat(order.userId()).isEqualTo(createOrder.userId());
    22.           assertThat(order.price()).isEuqalTo(100);
    23.     }
    24. }
    25. </font>
    复制代码

    对比一下argThat和@Captor两种测试Mock对象的方法参数的方法,使用@Captor这种方法具有:

    • 代码美观,修改容易;
    • 测试用例失败时能够快速定位出哪个参数不符合期望。


    1. <font size="3">org.opentest4j.AssertionFailedError:
    2. Expected :100
    3. Actual   :50
    4. </font>
    复制代码

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-4-28 01:34 , Processed in 0.065092 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表