在一个测试中,我使用了一个模拟服务。我想检查是否使用其中一个参数的特定属性调用了一次服务。然而,该方法也可以用其他参数调用多次,但我只对上面的一次调用感兴趣。
我的目的是用参数捕获器验证调用,以检查感兴趣的调用是否只调用一次。然而,这将失败,因为该方法将被多次调用,并且参数捕获器将在之后进行检查。
请看下面的例子:
// service method
void serviceMethod(String someString, MyType myType);
// parameter type
class MyType {
private String id;
...
String getID() {
return id;
}
}
// test verification
ArgumentCaptor<MyType> paramCaptor = ArgumentCaptor.forClass(MyType.class);
// fail in verify
Mockito.verify(serviceMock, times(1)).serviceMethod(eq("someConstantValue"), paramCaptor);
assertEquals("123", paramCaptor.getValue().getID());
我用hamcrest Matcher
解决了这个问题。匹配器在验证方法调用时检查参数值,而参数捕获器只读取参数以供以后求值。在这种情况下,具有未指定值的所有其他调用将被过滤掉。
static Matcher<MyType> typeIdIs(final String id) {
return new ArgumentMatcher<MyType>() {
@Override
public boolean matches(Object argument) {
return id.equals(((MyType)argument).getID());
}
};
}
Mockito.verify(serviceMock, times(1)).serviceMethod(eq("someConstantValue"), argThat(typeIdIs("123")));
您可以使用atLeastOnce()
和getAllValues()
的组合:
MyService service = mock( MyService.class );
ArgumentCaptor<MyType> captor = ArgumentCaptor.forClass( MyType.class );
service.serviceMethod( "foo", new MyType( "123" ) );
service.serviceMethod( "bar", new MyType( "312" ) );
service.serviceMethod( "baz", new MyType( "231" ) );
verify( service, atLeastOnce() ).serviceMethod( anyString(), captor.capture() );
List<String> ids = captor.getAllValues().stream().map( MyType::getId ).collect( Collectors.toList() );
assertThat( ids ).contains( "123" );
请注意,我使用静态导入以及AssertJ断言和匹配器,但您应该能够轻松地将其映射到JUnit或其他东西。
EDIT:如果您想确保只出现一次"123"
,您可以使用Collections#frequency( Collection, Object )
:
assertThat( Collections.frequency( ids, "123" ) ).isEqualTo( 1 );
或者更好:看一下AssertJ条件
如果您在Java 8中使用Mockito 2,有一种简洁的方法可以做到这一点。参见http://static.javadoc.io/org.mockito/mockito-core/2.2.9/org/mockito/Mockito.html 36
这个例子将被重构为:
// service method
void serviceMethod(String someString, MyType myType);
// parameter type
class MyType {
private String id;
...
String getID() {
return id;
}
}
// using a Java 8 lambda to test the ID within a custom ArgumentMatcher
// passed to argThat
// Note: you don't need to say "times(1)" as this assumes 1 time
// times(1) in the argument captor version would also confuse things
// if you had other calls and you
// were just checking for whether a call like THIS had happens
Mockito.verify(serviceMock).serviceMethod(eq("someConstantValue"),
argThat(input -> input.getID().equals("123")));
lambda使它更简洁,但您需要Java 8和Mockito 2。
EDIT:
我不知道怎么做你所要求的。
我喜欢@beatngu13的答案。
但是,另一个选项可能是将atLeastOnce()与有序验证结合使用:
InOrder inOrder = inOrder(serviceMock);
inOrder.verify(serviceMock, atLeastOnce()).serviceMethod(eq("someConstantValue"), paramCaptor);
inOrder.verify(serviceMock).serviceMethod(eq("someConstantValue"), fakeParamOne);
inOrder.verify(serviceMock).serviceMethod(eq("someConstantValue"), fakeParamTwo);
assertEquals("123", paramCaptor.getValue().getID());
,其中创建了一个fakeParam对象,该对象看起来类似于其他服务调用的参数