java 把Assert放在Mockito.argThat里面是好的做法吗?

gblwokeq  于 2023-03-28  发布在  Java
关注(0)|答案(2)|浏览(139)

假设你有一个使用Mockito的JUnit测试,你有一个argThat匹配器:

Mockito.argThat(obj -> {
  return "ok".equals(obj.getStuff()) ||
         "ok2".equals(obj.getStuff2()) ||
         "ok3".equals(obj.getStuff3()) ||
         "ok4".equals(obj.getStuff4());
});

但是现在当你运行测试失败时,它通常会失败,很难找出什么条件失败,你需要使用调试器。
如果我这样做:

Mockito.argThat(processedNuMessage -> {
  Assert.assertEquals("ok", obj.getStuff());
  Assert.assertEquals("ok2", obj.getStuff2());
  Assert.assertEquals("ok3", obj.getStuff3());
  Assert.assertEquals("ok4", obj.getStuff4());
  return true;
});

现在我得到了更容易使用的Assert。但如果感觉有点不稳定。
有没有更好的办法?

tv6aics1

tv6aics11#

不,它在ArgumentMatcher.matches文档中被 * 明确 * 标记为一种不良做法,在原始文档中强调:
如果参数不匹配,方法应该从不Assert。它应该只返回false。
这一点非常重要,原因如下:首先,如果有多个方法调用,一个调用可能会失败,而另一个调用可能会通过。通过抛出参数matcher,Mockito将在有机会匹配其他存根之前失败。此外,Mockito相信它可以在when等方法中调用此函数而不会产生副作用:

when(yourMock.yourMethod(anyInt(), argThat(new ThrowingMatcher()))).thenReturn(...);
// this next line will fail at stubbing time because of the previous line
when(yourMock.yourMethod(eq(6), argThat(new ThrowingMatcher()))).thenReturn(...);

在上面的第二个存根中,Mockito会在调用when之前调用yourMock.yourMethod,所以Mockito会尝试将match a call转换为yourMethod(0, null)。这将触发匹配器,由于不匹配,匹配器将抛出异常,阻止您进一步存根。这种不匹配只是一个例子:如果你从ArgumentMatcher中抛出并违反了接口契约,Mockito不能保证工作。
如果你想让Mockito更快或更清楚地失败,你有几个选择:

  • 对于存根时间,可以使用thenAnswer
when(yourMock.yourMethod(/* user= */ any())).thenAnswer(invocation -> {
  User user = invocation.getArgument(0);
  /* your asserts here */
  return yourReturnValue;
});
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
verify(yourMock).yourMethod(userCaptor.capture());
User user = userCaptor.getValue();
/* your asserts here */
  • 您可以启用Strictness,这将导致Mockito在未存根的方法调用上失败。这比使用抛出默认答案的旧Mockito 1.x技术更惯用。

注意:

  • 为用户提供一个合理的toString表示以进行调试可能会使您受益。helper libraries可用于此。ArgumentMatcher还记录了它在打印验证和失败匹配时使用其toString方法。
  • 取决于你匹配方法的仔细程度,你仍然可能遇到上面描述的存根问题。切换到doVerb方法,如doReturndoAnswer帮助,因为对doVerb的调用在对mock的调用之前,所以Mockito可以停用存根行为。
  • 虽然 Hamcrest matchers提供了一个看起来很吸引人的describeMismatch,但这在Mockito中通常没有价值,因为Mockito的调用匹配算法使得它更难判断调用何时“丢失”,而不是调用何时“不匹配”。它可能仍然是一个很好的解决方案。但是您必须使用上述技术之一手动调用Matcher,而不是只调用MockitoHamcrest.argThat。
9cbw7uwe

9cbw7uwe2#

你可以使用assertAll方法,代码看起来像例子:

User user = new User("baeldung", "support@baeldung.com", false);
assertAll(
  "Grouped Assertions of User",
  () -> assertEquals("admin", user.getUsername(), "Username should be admin"),
  () -> assertEquals("admin@baeldung.com", user.getEmail(), "Email should be admin@baeldung.com"),
  () -> assertTrue(user.getActivated(), "User should be activated")
);

更多关于它在这里-〉assetAllUsing。我想你可以找到答案是这个好的做法。顺便说一下,对我来说-是的,它是。
所以你的代码应该看起来像这样:

Mockito.argThat(obj -> {
  assertAll("obj",
    () -> assertEquals("ok", obj.getStuff()),
    () -> assertEquals("ok2", obj.getStuff2()),
    () -> assertEquals("ok3", obj.getStuff3()),
    () -> assertEquals("ok4", obj.getStuff4())
  );
  return true;
});

相关问题