Mockito 在更改行为时调用模拟方法一次
Mockito calls mocked method once time upon chanching the behavior
如果我在测试期间更改模拟行为,我会遇到一个非常奇怪的行为。我模拟了一个非常简单的界面:
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
次时,行为不会改变。期望增量为 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 次以上的行为后,使用模拟参数只调用了一次模拟方法?
在remocking 操作中调用bar.string(Mockito.anyString())
仍然等同于bar.string(String)
调用,之前被模拟以增加AtomicInteger
。
log.info("Result (1): " + bar.string("FOO_1")); // Increases to 1
Mockito.when(bar.string(Mockito.anyString())).then(invocation -> { // Increases to 2
改造后,由于新的mock生效,数字不再递增。您应该使用 clean mocks 以避免编写脆弱或复杂的测试代码。
如果我在测试期间更改模拟行为,我会遇到一个非常奇怪的行为。我模拟了一个非常简单的界面:
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
次时,行为不会改变。期望增量为 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 次以上的行为后,使用模拟参数只调用了一次模拟方法?
在remocking 操作中调用bar.string(Mockito.anyString())
仍然等同于bar.string(String)
调用,之前被模拟以增加AtomicInteger
。
log.info("Result (1): " + bar.string("FOO_1")); // Increases to 1
Mockito.when(bar.string(Mockito.anyString())).then(invocation -> { // Increases to 2
改造后,由于新的mock生效,数字不再递增。您应该使用 clean mocks 以避免编写脆弱或复杂的测试代码。