莫基托。验证方法参数
Posted
技术标签:
【中文标题】莫基托。验证方法参数【英文标题】: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")));
我总是通过考试。我怎样才能完成该验证(如果可能)?
谢谢。
【问题讨论】:
【参考方案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
匹配器
切勿将 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.
verify(mock).mymethod2("VALUE_1", argThat((x)->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)));
【讨论】:
以上是关于莫基托。验证方法参数的主要内容,如果未能解决你的问题,请参考以下文章
spring方法验证中如何验证方法参数默认为NotNull?