Mockito Matchers isA、any、eq 和 same 有啥区别?

Posted

技术标签:

【中文标题】Mockito Matchers isA、any、eq 和 same 有啥区别?【英文标题】:What's the difference between Mockito Matchers isA, any, eq, and same?Mockito Matchers isA、any、eq 和 same 有什么区别? 【发布时间】:2015-09-02 14:17:44 【问题描述】:

我对它们之间的区别以及在哪种情况下选择哪个感到困惑。有些区别可能很明显,例如 anyeq,但我将它们都包括在内只是为了确定。

我想知道它们的区别,因为我遇到了这个问题: 我在 Controller 类中有这个 POST 方法

public Response doSomething(@ResponseBody Request request) 
    return someService.doSomething(request);

并且想对该控制器执行单元测试。 我有两个版本。第一个是简单的,像这样

@Test
public void testDoSomething() 
    //initialize ObjectMapper mapper
    //initialize Request req and Response res
    
    when(someServiceMock.doSomething(req)).thenReturn(res);

    Response actualRes = someController.doSomething(req);
    assertThat(actualRes, is(res));

但我想使用 MockMvc 方法,比如这个

@Test
public void testDoSomething() 
    //initialize ObjectMapper mapper
    //initialize Request req and Response res
    
    when(someServiceMock.doSomething(any(Request.class))).thenReturn(res);

    mockMvc.perform(post("/do/something")
            .contentType(MediaType.APPLICATION_JSON)
            .content(mapper.writeValueAsString(req))
    )
            .andExpect(status().isOk())
            .andExpect(jsonPath("$message", is("done")));

两者都运作良好。但我希望我的someServiceMock.doSomething() 在 MockMvc 方法中接收req,或者至少是一个与req 具有相同变量值的对象(不仅仅是任何Request 类),并返回res,就像首先。我知道使用 MockMvc 方法是不可能的(或者是吗?),因为在实际调用中传递的对象总是与在模拟中传递的对象不同。无论如何我可以做到这一点吗?或者这样做是否有意义?或者我应该对使用any(Request.class) 感到满意吗?我试过eqsame,但都失败了。

【问题讨论】:

【参考方案1】:

any() 绝对不会检查任何内容。从 Mockito 2.0 开始,any(T.class) 共享 isA 语义以表示“任何T”或正确的“任何T 类型的实例”。

This is a change compared to Mockito 1.x,其中any(T.class) 在 Java 8 之前完全没有检查,只是保存了一个强制转换:“任何种类的对象,对于给定的类不是必需的。提供 class 参数只是为了避免强制转换。”

isA(T.class) 检查参数instanceof T,暗示它是非空的。

same(obj) 检查参数是否与obj 引用相同的实例,因此arg == obj 为真。

eq(obj) 根据其equals 方法检查参数是否等于obj。如果您在不使用匹配器的情况下传递实际值,这也是这种行为。

请注意,除非 equals 被覆盖,否则您将看到默认的 Object.equals 实现,其行为与 same(obj) 相同。

如果您需要更精确的自定义,您可以为自己的谓词使用适配器:

对于 Mockito 1.x,将 argThat 与自定义 Hamcrest Matcher<T> 一起使用,以准确选择您需要的对象。 对于 Mockito 2.0 及更高版本,使用 Matchers.argThat 和自定义 org.mockito.ArgumentMatcher<T>,或 MockitoHamcrest.argThat 和自定义 Hamcrest Matcher<T>

您也可以使用refEq,它使用反射来确认对象相等; Hamcrest 与 SamePropertyValuesAs 有类似的实现,用于公共 bean 样式属性。请注意,在 GitHub issue #1800 proposes deprecating and removing refEq 上,就像在那个问题中一样,您可能更喜欢 eq,以便更好地为您的类提供更好的封装,而不是它们的平等感。

【讨论】:

这是一个实用的答案:不仅可以区分 Mockito 的 4 个 参数匹配器,还可以用 equalscustomization 提示 风险 b> 与argThat ? 请添加refEq() 当未在比较对象上实现 equals() 时,可以使用此匹配器。 Matcher 使用 java 反射 API 来比较想要和实际对象的字段。 @Ivan 虽然它不在最初的问题中,但我已经提到了refEq,包括the proposal to deprecate and remove it from Mockito。 那么,我们应该为最新版本使用什么? @Robert 上面的列表告诉你它们之间的语义差异。您必须将它们与您的用例相匹配。【参考方案2】:

如果你的 Request.class 实现了 equals,那么你可以使用 eq():

Bar bar = getBar();
when(fooService.fooFxn(eq(bar)).then...

上面的什么时候会激活

fooService.fooFxn(otherBar);

如果

otherBar.equals(bar);

或者,如果您希望模拟适用于其他输入子集(例如,所有带有 Bar.getBarLength()>10 的 Bars),您可以创建一个 Matcher。我不经常看到这种模式,所以通常我将 Matcher 创建为私有类:

private static class BarMatcher extends BaseMatcher<Bar>
...//constructors, descriptions, etc.
  public boolean matches(Object otherBar)
     //Checks, casts, etc.
     return otherBar.getBarLength()>10;
  

然后您将按如下方式使用此匹配器:

when(fooService.fooFxn(argThat(new BarMatcher())).then...

希望有帮助!

【讨论】:

以上是关于Mockito Matchers isA、any、eq 和 same 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Mockito的参数匹配

类型“Matchers<any>”上不存在属性“toBeInTheDocument”

使用 Mockito 的通用“any()”方法

在“使用”标准库函数中测试 Kotlin lambda

Mockito.anyString()在Kotlin中与NPE崩溃

带有 scala 的 mockito:匹配器问题