【问题标题】:Mockito calls mocked method once time upon chanching the behaviorMockito 在改变行为时调用一次模拟方法
【发布时间】:2020-11-11 23:57:22
【问题描述】:

如果我在测试期间更改模拟行为,我遇到了一个非常奇怪的行为。我模拟了一个非常简单的界面:

interface Bar { 
    String string(String str); 
}

@Mock
private Bar bar;

然后我调用它并使用AtomicInteger 计算调用次数,这是这个最小工作示例的副作用。

@Test
public void test() {

    AtomicInteger atomicInteger = new AtomicInteger(0);

    // Mock with the increment
    Mockito.when(bar.string(Mockito.anyString())).then(invocation -> {    
        log.info("MOCK - waiting (1): {}", invocation.getArguments()[0]);
        atomicInteger.incrementAndGet();
        log.info("MOCK - returning (1)");
        return "BAR_1";
    });
    // Invocation of the increment
    log.info("Result (1): " + bar.string("FOO_1"));                       

    // Passes
    Assertions.assertEquals(1, atomicInteger.get());                      
}
14:18:17.336 [main] INFO com.Foo - MOCK - waiting (1): FOO_1
14:18:17.343 [main] INFO com.Foo - MOCK - returning (1)
14:18:17.349 [main] INFO com.Foo - Result (1): BAR_1

只要使用bar.string("FOO_1") 明显调用了一次该方法,测试就会通过。只要我在执行此操作后添加模拟bar 的新行为而不增加AtomicInteger,就会再次调用不应调用的原始模拟:

@Test
public void test() {

    AtomicInteger atomicInteger = new AtomicInteger(0);

    // Mock with the increment
    Mockito.when(bar.string(Mockito.anyString())).then(invocation -> {
        log.info("MOCK - waiting (1): {}", invocation.getArguments()[0]);
        atomicInteger.incrementAndGet();
        log.info("MOCK - returning (1)");
        return "BAR_1";
    });

    // Invocation with increment
    log.info("Result (1): " + bar.string("FOO_1"));

    /* NEW CODE BLOCK STARTS */

    // Mock without the increment
    Mockito.when(bar.string(Mockito.anyString())).then(invocation -> {    
         log.info("MOCK - returning (2): {}", invocation.getArguments()[0]);
         return "BAR_2";
    });

    // Invocation without the increment
    // The previous lines really changed the mock, but it was called one more times
    log.info("Result (2): " + bar.string("FOO_2"));

    /* NEW CODE BLOCK ENDS */

    // Fails, it is 2
    Assertions.assertEquals(1, atomicInteger.get());                      
}
14:19:31.603 [main] INFO com.Foo - MOCK - waiting (1): FOO_1
14:19:31.612 [main] INFO com.Foo - MOCK - returning (1)
14:19:31.620 [main] INFO com.Foo - Result (1): BAR_1
14:19:31.621 [main] INFO com.Foo - MOCK - waiting (1): 
14:19:31.621 [main] INFO com.Foo - MOCK - returning (1)
14:19:31.623 [main] INFO com.Foo - MOCK - returning (2): FOO_2
14:19:31.624 [main] INFO com.Foo - Result (2): BAR_2

令人惊讶的是,日志显示在第 4 行调用模拟方法时没有参数。

当我在同一测试N-times 中包含更多此代码块时,行为不会改变。预期增量为 2 而不是 1 时,测试总是失败。

Mockito.when(bar.string(Mockito.anyString())).then(invocation -> {
     log.info("MOCK - returning (N): {}", invocation.getArguments()[0]);
     return "BAR_N";
});
log.info("Result (N): " + bar.string("FOO_N"));                       

是什么让 Mockito 在测试期间更改了 1 次以上的行为后,使用模拟参数调用了模拟方法一次?

【问题讨论】:

  • 您实际上并没有手动编写调用计数和参数检查,是吗?他们有现成的机制。
  • @Kayaman:这无关紧要。当涉及到副作用时,我通过一个最小的工作示例展示了 Mockito 的奇怪行为。将AtomicInteger::incrementAndGet 视为一种副作用方法。
  • 你在 remocking 一种方法来做一些不必要的事情。我需要验证你是否意识到你正在做一些奇怪的事情,所以你也不应该对你得到奇怪的结果感到惊讶。 correct way 将重置或重建您的模拟,此结果的原因在于内部实现。
  • 我正在移除一种方法以不产生副作用(因此没有增量)并演示原始模拟方法(带增量)以某种方式被调用两次,尽管它应该在被调用一次时只调用一次.第二次调用发生在 remocked 方法上,没有副作用(没有增量)。实际上,原来的方法还是又被调用了一次。我知道我在做什么,这就是为什么我对它给我的结果感到惊讶。您如何解释第二个日志中的第 4 行?
  • 为什么你认为bar.string("FOO_1") 增加了AtomicIntegerbar.string(Mockito.anyString()) 当你试图remock 方法时没有?他们都调用了增加数字的模拟方法,这就是为什么你不应该尝试重新模拟该方法!这是一个方法调用,唯一绝对不会有副作用的地方是使用 empty 模拟,之前模拟的方法是 not 为空。

标签: java mocking mockito junit5


【解决方案1】:

在remocking操作中调用bar.string(Mockito.anyString())仍然等价于bar.string(String)调用,之前被mock了增加AtomicInteger

log.info("Result (1): " + bar.string("FOO_1"));  // Increases to 1

Mockito.when(bar.string(Mockito.anyString())).then(invocation -> {  // Increases to 2

remocking 后,数字不再增加,因为新的 mock 已经生效。您应该使用clean mocks 以避免编写脆弱或复杂的测试代码。

【讨论】:

  • 我已经解决了Mockito.when(..).then(..).then(..)链的问题。
猜你喜欢
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2017-07-05
  • 2017-05-17
  • 2017-06-03
相关资源
最近更新 更多