使用 Mockito 2 模拟服务会导致存根错误

2022-09-01 02:55:22

我尝试使用Mockito模拟班级的行为。这使用Mockito 1.x工作。迁移到JUnit 5和Mockito 2似乎不再有效。

@ExtendWith(MockitoExtension.class)
public class MockitoExample {

  static abstract class TestClass {
    public abstract int booleanMethod(boolean arg);
  }

  @Mock
  TestClass testClass;

  @BeforeEach
  public void beforeEach() {
    when(testClass.booleanMethod(eq(true))).thenReturn(1);
    when(testClass.booleanMethod(eq(false))).thenReturn(2);
  }

  @Test
  public void test() {
    assertEquals(1,testClass.booleanMethod(true));
    assertEquals(2,testClass.booleanMethod(false));
  }
}

期望是,模拟的 TestClass 显示测试方法中测试的行为。

我得到的错误是:

org.mockito.exceptions.misusing.PotentialStubbingProblem: 

  Strict stubbing argument mismatch. Please check:
   - this invocation of 'booleanMethod' method:
      testClass.booleanMethod(false);
      -> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:30)
   - has following stubbing(s) with different arguments:
      1. testClass.booleanMethod(false);
        -> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:29)
  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.

在这两种情况下,论点似乎都匹配,尽管我显然与 匹配。falsetrue

这是Mockito 2.17中的错误还是误解。我应该如何使用Mockito 2.x来模拟具有不同布尔参数的调用?

示例也可以在github上找到。但 Surefire 将仅使用以下命令开始测试

mvn test -Dtest=MockitoExample

使用 Mockito 2.21 执行测试会产生相同的结果。


答案 1

由于Mockito 2.20,也可以在本地添加lenient()

@ExtendWith(MockitoExtension.class)
public class MockitoExample {

  static abstract class TestClass {
    public abstract int booleanMethod(boolean arg);
  }

  @Mock
  TestClass testClass;

  @BeforeEach
  public void beforeEach() {
    lenient().when(testClass.booleanMethod(eq(true))).thenReturn(1);
    lenient().when(testClass.booleanMethod(eq(false))).thenReturn(2);
  }

  @Test
  public void test() {
    assertEquals(1,testClass.booleanMethod(true));
    assertEquals(2,testClass.booleanMethod(false));
  }
}

答案 2

使用严格的存根(Mockito的默认行为),在同一方法上调用多个s将重置该模拟。解决方案是调用一次,并将逻辑放在:whenwhenAnswer

@BeforeEach
public void beforeEach() {
    when(testClass.booleanMethod(anyBoolean())).thenAnswer(invocationOnMock -> {
        if ((boolean) invocationOnMock.getArguments()[0]) {
            return 1;
        }
        return 2;
    });
}

或者,您可以使用宽容的嘲笑,但这并不总是一个好主意 - 宽容的嘲笑允许多余的存根,并使您更容易在测试中犯错误,这可能会导致“生产”代码中未被注意到的错误:

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class MockitoExample {

推荐