我正在用JUnit 4和Mockito 4.6.1编写一个单元测试。我正在模拟一个具有特定输入值的接口方法。当传递不同的值时,我预计会出现参数不匹配错误,但它没有被抛出。
考虑下面的例子:
public class SomeTest {
@Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, Mockito.withSettings().strictness(Strictness.STRICT_STUBS));
// Only mock test(true) and not test(false).
Mockito.when(mock.test(true)).thenReturn(1);
Assert.assertEquals(mock.test(true), 1);
Assert.assertEquals(
// Expecting argument mismatch error
mock.test(false),
2
);
}
interface SomeInterface {
int test(Boolean arg);
}
}
我嘲笑SomeInterface.test(true)
返回1。这工作如预期。现在,当我调用mock.test(false)
时,我预计会出现参数不匹配,因为它没有在模拟设置中定义,并且启用了严格模式。相反,它返回0,就好像它被模拟了。
我错过了导致这种情况发生的东西吗?
问题1我们必须为测试启用strict_stub。
strict_stub -确保干净的测试,减少测试代码重复,提高debuggability。最佳组合的灵活性和生产力。强烈推荐。作为Mockito v4的默认计划。通过MockitoRule, MockitoJUnitRunner或MockitoSession启用它。看到
根据文档,可以通过MockitoJUnitRunner.StrictStubs.class
@RunWith(MockitoJUnitRunner.StrictStubs.class)
或严格规则
@Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
问题2。当模拟定义和模拟调用在一个源类中执行时,参数严格性不起作用。
根据Mockito源代码:
如果存根和调用在同一个源文件中,我们假定它们在同一个源文件中测试代码,我们不把它标记为mismatch
总结。工作测试将是:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class SomeTest {
@Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, withSettings().strictness(Strictness.STRICT_STUBS));
Mockito.when(mock.test(true)).thenReturn(1);
SomeInterfaceImpl someInterface = new SomeInterfaceImpl(mock);
Assert.assertEquals(someInterface.test(), 2);
}
}
public interface SomeInterface {
int test(Boolean arg);
}
public class SomeInterfaceImpl {
private SomeInterface someInterface;
public SomeInterfaceImpl(SomeInterface someInterface) {
this.someInterface = someInterface;
}
public int test() {
return someInterface.test(false);
}
}
执行时得到:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'test' method:
someInterface.test(false);
-> at com.example.SomeInterfaceImpl.test(SomeInterfaceImpl.java:10)
- has following stubbing(s) with different arguments:
1. someInterface.test(true);
-> at com.example.SomeTest.test(SomeTest.java:26)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
Please use 'will().given()' or 'doReturn().when()' API for stubbing.
- stubbed method is intentionally invoked with different arguments by code under test
Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
For more information see javadoc for PotentialStubbingProblem class.
更好的解决方案
如果使用不匹配的参数执行mock,则可以使用IllegalArgumentException
为mock定义默认答案。此解决方案将始终在没有源文件限制的情况下工作。
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.doReturn;
@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
@Test
public void test() {
SomeInterface mock = Mockito.mock(SomeInterface.class, Mockito.withSettings().defaultAnswer(
invocation -> {
throw new IllegalArgumentException(String.format("You cannot invoke %s with %s", invocation.getMethod(), java.util.Arrays.toString(invocation.getArguments())));
}
));
// Only mock test(true) and not test(false).
doReturn(1).when(mock).test(true);
Assert.assertEquals(mock.test(true), 1);
Assert.assertEquals(
// Expecting argument mismatch error
mock.test(false),
2
);
}
}
执行时得到:
java.lang.IllegalArgumentException: You cannot invoke public abstract int com.example.SomeInterface.test(java.lang.Boolean) with [false]