【问题标题】:Mockito. Verify method arguments莫基托。验证方法参数
【发布时间】:2011-04-03 02:21:55
【问题描述】:

我用谷歌搜索过这个,但没有找到任何相关的东西。我有这样的东西:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

现在,我想验证在runtestmethod() 内部调用的mymethod(Object o) 是用对象o 调用的,而不是其他对象。但我总是通过测试,无论我在验证中添加什么,例如:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

Mockito.verify(mock.mymethod(Mockito.eq(null)));

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

我总是通过考试。我怎样才能完成该验证(如果可能)?

谢谢。

【问题讨论】:

    标签: java unit-testing junit mockito


    【解决方案1】:

    ArgumentMatcher 的替代品是ArgumentCaptor

    官方示例:

    ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
    verify(mock).doSomething(argument.capture());
    assertEquals("John", argument.getValue().getName());
    

    也可以使用@Captor 注解来定义捕获者:

    @Captor ArgumentCaptor<Person> captor;
    //... MockitoAnnotations.initMocks(this);
    @Test public void test() {
        //...
        verify(mock).doSomething(captor.capture());
        assertEquals("John", captor.getValue().getName());
    }
    

    【讨论】:

    • 感谢样品!从来没有用过。在代码中包含 captor 之类的东西感觉有点奇怪,但它有帮助。
    • 哈哈,这个问题我没看懂,不过回答对我帮助很大。谢谢:-)
    • 重要:使用模拟调用 verify()/capture() after。我在想它必须先“安装”...
    • 感谢您的回答!
    • 这是一个很好的答案!!非常感谢!
    【解决方案2】:

    您是否尝试使用对象的 .equals 方法进行逻辑相等?您可以使用 Mockito 中包含的 argThat 匹配器来完成此操作

    import static org.mockito.Matchers.argThat
    

    接下来,您可以实现自己的参数匹配器,该匹配器将遵循每个对象的 .equals 方法

    private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
        T thisObject;
    
        public ObjectEqualityArgumentMatcher(T thisObject) {
            this.thisObject = thisObject;
        }
    
        @Override
        public boolean matches(Object argument) {
            return thisObject.equals(argument);
        }
    }
    

    现在使用您的代码,您可以更新它以读取...

    Object obj = getObject();
    Mockeable mock= Mockito.mock(Mockeable.class);
    Mockito.when(mock.mymethod(obj)).thenReturn(null);
    
    Testeable obj = new Testeable();
    obj.setMockeable(mock);
    command.runtestmethod();
    
    verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));
    

    如果你只是为了精确相等(内存中的相同对象),就这样做

    verify(mock).mymethod(obj);
    

    这将验证它被调用过一次。

    【讨论】:

    • 您可以为此目的使用ReflectionEquals 类中的构建。
    • +1 为您解答。但我想补充一点,verify(mock).mymethod(obj); 不检查完全相等(内存中的相同对象)。相反,它使用了可能被覆盖的对象等方法。
    • 您还可以创建ArgumentMatcher 的匿名实现以减少冗长。
    • 更多细节:默认情况下,verify() 调用 /inbound 参数的/equals() 方法,而不是 /recorded 对象的/equals() 方法。这无关紧要,除非您试图确认您的测试主体返回一个特定的对象实例,并且该主体返回应该是该实例的透明装饰器。 verify 参数的 equals() 不会知道装饰器;而装饰者的equals() 将被重写以容忍原件。在这种情况下,您的测试将错误地失败。
    【解决方案3】:

    argThat加lambda

    这就是你的论证验证失败的原因:

        verify(mock).mymethod(argThat(
                                x -> false ));
    

    在哪里

    import static org.mockito.ArgumentMatchers.argThat;
    import static org.mockito.Mockito.verify;
    

    argThat 加断言

    上面的测试会“说”Expected: lambda$... Was: YourClass.toSting...。如果在 lambda 中使用断言,您可以获得更具体的失败原因:

        verify(mock).mymethod(argThat( x -> {
          assertThat(x).isNotNull();
          assertThat(x.description).contains("KEY");
          return true;
        }));
    

    ❗️但是❗️:只有在

    • 预计会调用 1 次,或者
    • 预计调用 2 次以上,但验证器始终匹配(返回 true)。

    如果验证的方法调用了 2 次以上,mockito 会将所有调用的组合传递给每个验证者。所以 mockito 期望您的验证器为参数集之一静默返回 true,并为其他有效调用返回 false(无断言异常)。这种期望对于 1 次方法调用来说不是问题 - 它应该只返回 true 1 次。

    import static org.assertj.core.api.Assertions.assertThat;
    import static org.mockito.ArgumentMatchers.argThat;
    import static org.mockito.Mockito.verify;
    

    现在失败的测试将显示:Expected: Obj.description to contain 'KEY'. Was: 'Actual description'。注意:我使用了assertJ 断言,但使用哪个断言框架取决于您。


    直接参数

    Mokito 使用 equals() 比较直接参数:

    verify(mock).mymethod(expectedArg);
    // NOTE:   ^ where the parentheses must be closed.
    

    eq匹配器


    argThat 带有多个参数。

    如果您使用argThat,则必须为所有参数提供匹配项。例如。如果,在不同的情况下,你有另一个带有 2 个参数的方法:

        verify(mock).mymethod2(eq("VALUE_1"), argThat((x)->false));
        // above is correct as eq() is also an argument matcher.
    

    verify(mock).mymethod2("VALUE_1", argThat((x)-&gt;false));

    // above is incorrect; an exception will be thrown, as the first arg. is given without an argument matcher.

    地点:

    import static org.mockito.ArgumentMatchers.argThat;
    import static org.mockito.ArgumentMatchers.eq;
    import static org.mockito.Mockito.verify;
    

    原始问题失败的根本原因是括号的错误位置:

    • verify(mock.mymethod...。那是错误的。正确的是:
    • verify(mock).*

    【讨论】:

    • 这是我最喜欢的答案,比其他答案更有效。
    • 这是如何工作的?验证(模拟).mymethod(eq(“VALUE_1”),argThat((x)->false)); “mymethod”有一个 arg 我们如何发送两个?
    • @max,没错。该示例位于 ... with multiple arguments 部分下,因此,是的,您是对的,它与原始 mymethod(arg0) 案例无关。它只对不同的(2 args)案例有意义。 将其重命名为 mymethod2,以避免混淆,一点点
    【解决方案4】:
    • 如果您不使用其他匹配器,则不需要 eq 匹配器。
    • 您没有使用正确的语法 - 您的方法调用应该在 .verify(mock) 之外。您现在正在对方法调用的结果进行验证,而不验证任何内容(不进行方法调用)。因此所有测试都通过了。

    您的代码应如下所示:

    Mockito.verify(mock).mymethod(obj);
    Mockito.verify(mock).mymethod(null);
    Mockito.verify(mock).mymethod("something_else");
    

    【讨论】:

    • 我之前尝试过,现在再次确定。我还是有同样的问题,测试总是通过。
    • 通过引用验证
    • @cnexans ,不,它不会通过引用进行验证。此外,eq 将是 SonarQube/SonarCloud 代码气味警报:rules.sonarsource.com/java/tag/mockito/RSPEC-6068
    【解决方案5】:

    我曾以这种方式使用过 Mockito.verify

    @UnitTest
    public class JUnitServiceTest
    {
        @Mock
        private MyCustomService myCustomService;
    
    
        @Test
        public void testVerifyMethod()
        {
           Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
           Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
           Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
           Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
           Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
           Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
        }
    }
    

    【讨论】:

      【解决方案6】:

      你检查过可模拟类的 equals 方法吗?如果这个总是返回 true 或者你针对同一个实例测试同一个实例并且 equal 方法没有被覆盖(因此只检查引用),那么它返回 true。

      【讨论】:

        【解决方案7】:

        另一种方法是使用 org.mockito.internal.matchers.Equals.Equals 方法而不是重新定义一个:

        verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
        

        【讨论】:

          【解决方案8】:

          您是否尝试过使用相同的匹配器?如:

          verify(mockObj).someMethod(same(specificInstance));
          

          我遇到了同样的问题。我尝试使用 eq() 匹配器和 refEq() 匹配器,但我总是误报。当我使用 same() 匹配器时,当参数是不同的实例时测试失败,并且一旦参数是相同的实例就通过了。

          【讨论】:

            【解决方案9】:

            上述许多答案让我感到困惑,但我怀疑这可能是由于旧版本的 Mockito 造成的。这个答案是使用

            完成的
            • Java 11
            • Mockito 3.1.0
            • SpringBoot 2.2.7.RELEASE
            • JUnit5

            使用 ArgumentCaptor 我已经这样做了:

            @Mock
            MyClientService myClientService;
            @InjectMocks 
            MyService myService;
            
            
            @Test
            void myTest() {
            
              ArgumentCaptor<String> captorParam1 = ArgumentCaptor.forClass(String.class);
              ArgumentCaptor<String> captorParam2 = ArgumentCaptor.forClass(String.class);
            
              Mockito.when(myClientService.doSomething(captorParam1.capture(), captorParam2.capture(), ArgumentMatchers.anyString()))
                  .thenReturn(expectedResponse);
            
              assertDoesNotThrow(() -> myService.process(data));
            
              assertEquals("param1", captorParam1.getValue());
              assertEquals("param2", captorParam2.getValue());
            
              verify(myClientService, times(1))
                .doSomething(anyString(), anyString(), anyString());
            }
            

            【讨论】:

              【解决方案10】:
              Verify(a).aFunc(eq(b))
              

              在伪代码中:

              当在a 实例中时 - 调用名为 aFunc 的函数。

              验证这个调用有一个等于b的参数。

              【讨论】:

                【解决方案11】:

                你也可以使用 TypeSafeDiagnosingMatcher

                    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
                    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {
                
                        StringBuilder text = new StringBuilder(500);
                
                        @Override
                        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
                            String productCode = req.getPackageIds().iterator().next().getValue();
                            if (productCode.equals(request.getSupplierProductCode())) {
                                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                                return true;
                            }
                
                            text.append(req.toString());
                            return false;
                        }
                
                        @Override
                        public void describeTo(Description d) {
                            d.appendText(text.toString());
                        }
                    };
                }
                

                然后验证调用:

                Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
                

                【讨论】:

                  猜你喜欢
                  • 2013-01-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多