ChatGPT解决这个技术问题 Extra ChatGPT

莫基托。验证方法参数

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

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")));

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

谢谢你。


N
Nikunj Undhad

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()。我在想它必须先“安装”...
感谢您的回答!
这是一个很好的答案!非常感谢!
e
epox

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 用于单个 arg。使用前面提到的直接论点。

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

原因:eq 将违反 SonarQube / SonarClound:https://rules.sonarsource.com/java/tag/mockito/RSPEC-6068

argThat 带有多个参数。

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

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

验证(模拟).mymethod2(“VALUE_1”,argThat((x)->false)); // 上面不正确;将抛出异常,作为第一个参数。没有参数匹配器。

在哪里:

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

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

verify(mock.mymethod.... 这是错误的。正确的是:

验证(模拟)。*


这是我最喜欢的答案,比其他答案更有效且更优雅。
这是怎么工作的?验证(模拟).mymethod(eq(“VALUE_1”),argThat((x)->false)); “mymethod”有一个参数我们如何发送两个?
@max,正确的抓住。该示例位于 ... with multiple arguments 部分下,因此,是的,您是对的,它与原始 mymethod(arg0) 案例无关。它只对不同的(2 args)案例有意义。 将它重命名为 mymethod2,以避免混淆,一点点
M
Matthew Kirkley

您是否尝试使用对象的 .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() 将被重写以容忍原件。在这种情况下,您的测试将错误地失败。
B
Bozho

如果您不使用其他匹配器,则不需要 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
F
Free-Minded

我以这种方式使用了 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
    }
}

r
rit

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


N
Nils Renaud

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

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

W
Will

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

爪哇 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());
}

c
cbbcloud

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

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

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


L
Labu
Verify(a).aFunc(eq(b))

在伪代码中:

当在实例 a - 一个名为 aFunc 的函数被调用时。验证此调用是否有一个等于 b 的参数。


R
Ray

您还可以使用 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)));