2016-10-26 30 views
1

在测试中,我使用模拟服务。我想检查一下服务是否有一个参数的特定属性被调用。 但是,该方法也被称为与其他参数的多次,但我只对上面的一个调用感兴趣。使用ArgumentCaptor验证模拟服务调用失败

我的意图是验证与参数捕获器的调用,以检查感兴趣的调用只被调用一次。 但是这会失败,因为该方法被多次调用,并且之后检查参数捕获器。

见下面的例子:

// 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()); 

回答

3

我用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"))); 
+1

这是一个很好的方法。一般来说,争辩者是一种反模式。你应该可以通过验证来解决这个问题。 –

-1

编辑:

我不知道的方式做你问什么。

我喜欢@ 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对象,看起来像你的参数有关的其他呼叫到

服务。

+0

我想验证该服务被称为唯一的一次* *使用指定的参数集。 – Stefan

2

你可能用的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"); 

请注意,我用static imports以及AssertJ断言和匹配器,但你应该能够很容易地这个映射到的JUnit或别的东西。

编辑:如果你想确保有完全的"123"一个发生,你可以使用Collections#frequency(Collection, Object)

assertThat(Collections.frequency(ids, "123")).isEqualTo(1); 

甚至更​​好:看看AssertJ conditions

+0

谢谢你的回答。从未想过为以后的检查收集所有的值。我用一个hamcrest匹配器解决了这个问题。 – Stefan

+0

@Stefan不客气。增加了另一种方法来确保ID只出现一次。 – beatngu13

1

如果您在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"))); 

拉姆达使得它更干净,但你需要的Java 8和2的Mockito