代码之家  ›  专栏  ›  技术社区  ›  aschoerk

使用mockito2模拟服务导致存根错误

  •  6
  • aschoerk  · 技术社区  · 6 年前

    我尝试用Mockito模拟类的行为。 这在mockito1.x中起到了作用。迁移到junit5和mockito2后,它似乎不再起作用了。

    @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));
      }
    }
    

    我们的期望是,mocked 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.
    

    在这两种情况下,论点 false 似乎是匹配的,虽然我和 true .

    这是Mockito2.17中的错误还是误解。我应该/可以如何使用mockito2.x来模拟具有不同布尔参数的调用?

    这个 example 也可以在github上找到。但是surefire只会使用

    mvn test -Dtest=MockitoExample
    

    使用mockito2.21执行测试会得到相同的结果。

    4 回复  |  直到 5 年前
        1
  •  25
  •   Mureinik    6 年前

    使用严格存根(Mockito的默认行为)调用 when 相同方法上的将重置该模拟。解决办法是打电话 什么时候 一旦 Answer :

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

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

    @ExtendWith(MockitoExtension.class)
    @MockitoSettings(strictness = Strictness.LENIENT)
    public class MockitoExample {
    
        2
  •  20
  •   aschoerk    6 年前

    @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));
      }
    }
    
        3
  •  10
  •   davidxxx    6 年前

    mockito1和2没有相同的“严格”级别。
    此外,通过使用mockito2和junit4或5,默认级别仍然会有所不同。

    3个严格级别:

    • LENIENT
    • WARN :向控制台发出额外警告
    • STRICT_STUBS :通过在潜在的误用情况下引发异常来确保干净的测试,但也可能产生一些误报。

    • 莫基托1: 宽大
    • Mockito 2和JUnit 4:
    • Mockito 2和JUnit 5( MockitoExtension.class ) : 严格的\u存根
    • 严格的\u存根 .

    更多细节

    这个 Strictness javadoc 国家:

    在模拟会话期间配置Mockito的“严格性” 会话通常映射到单个测试方法调用。严格 推动更清洁的测试和更好的生产力最简单的方法 利用Mockito的JUnit支持增强的严格性 这是一个很好的方法。

    会话)?

    Strictness.LENIENT -未添加行为学仅当不能使用严格的\u存根或警告时,才建议使用默认的Mockito 1.x。

    Strictness.WARN -有助于保持测试清洁并提高可调试性报告控制台关于未使用的存根和存根的警告 参数不匹配(请参见org.mockito.quality公司.MockitoHint)。默认值 使用JUnitRule或MockitoJUnitRunner时mockito2.x的行为。 如果不能使用严格的\u存根,建议使用。

    Strictness.STRICT_STUBS -确保干净的测试,减少测试代码重复,提高可调试性最好灵活性的结合 有关详细信息,请参见严格的\u存根。

    但是不管抛出的异常与消息相关联

    似乎检查得太严了。 异常消息以某种方式证明:

    但是,当此异常生成false时,有些情况是合法的 负信号:

    ...

    • stubbed方法是被测试代码故意用不同的参数调用的

    因此,默认情况下禁止这样做似乎太过分了。
    因此,如果您使用JUnit5作为 严格的\u存根 你可以用 WARNING 但你一般都想避免 宽大 太安静了。

    除了 MockitoExtension mockito-junit-jupiter 图书馆提供 @MockitoSettings

    举个例子:

    import java.util.List;
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mockito;
    import org.mockito.junit.jupiter.MockitoExtension;
    import org.mockito.junit.jupiter.MockitoSettings;
    import org.mockito.quality.Strictness;
    
    @ExtendWith(MockitoExtension.class)
    public class FooTest {
    
        @MockitoSettings(strictness = Strictness.WARN)
        @Test
        void foo() throws Exception {
            List<String> strings = Mockito.mock(List.class);
            Mockito.when(strings.add("a"))
                   .thenReturn(true);
            Mockito.when(strings.add("b"))
                   .thenReturn(false);
        }
    
        @Test
        void fooKo() throws Exception {
            List<String> strings = Mockito.mock(List.class);
            Mockito.when(strings.add("a"))
                   .thenReturn(true);
            Mockito.when(strings.add("b"))
                   .thenReturn(false);
    
        }
    
    }
    

    fooKo() foo() 已成功,但提供了有用的警告:

    [MockitoHint] FooTest (see javadoc for MockitoHint):
    [MockitoHint] 1. Unused -> at FooTest.foo(FooTest.java:19)
    [MockitoHint] 2. Unused -> at FooTest.foo(FooTest.java:21)
    

    作为其他选择,您也可以使用 Mockito.lenient() 很好的描述 aschoerk对特定的调用应用宽严。

    @Test
    void foo() throws Exception {
        List<String> strings = Mockito.mock(List.class, Mockito.withSettings()
                                                               .lenient());
         ....
    }
    
        4
  •  -4
  •   johanneslink    6 年前

    由于第一个答案出乎意料,我检查了以下内容:

    interface Poops {
        String get(boolean is);
    }
    
    @Test
    void test1() {
        Poops a = mock(Poops.class);
    
        when(a.get(eq(true))).thenReturn("1");
        when(a.get(eq(false))).thenReturn("2");
    
        Assertions.assertEquals("1", a.get(true));
        Assertions.assertEquals("2", a.get(false));
    }
    

    它适用于mockito2.21.0。

    : 问题似乎是Jupiter Mockito扩展将默认设置更改为 Strictness.STRICT_STUBS