Java Mockito 可以验证参数具有某些属性/字段吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31993439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:53:26  来源:igfitidea点击:

Can Mockito verify an argument has certain properties/fields?

javaunit-testingmockingmockito

提问by Captain Man

Say I am mocking this class Foo

说我在嘲笑这个班级 Foo

class Foo {
  public void doThing(Bar bar) {
    // ...
  }
}

and this is Bar

这是 Bar

class Bar {
  private int i;
  public int getI() { return i; }
  public void setI(int i) { this.i = i; }
}

I know I can use Mockito's verify functionality to see if Foo#doThing(Bar)was called on the mock with a specific instance of Baror any Barwith Mockito.any(Bar.class), but is there some way to ensure it was called by any Barbut with a specific value for ior Bar#getI()?

我知道我可以使用 Mockito 的验证功能来查看是否Foo#doThing(Bar)在具有特定实例的Bar或任何 Barwith的模拟上调用Mockito.any(Bar.class)但是有什么方法可以确保它被任何Bar但具有特定值的i或调用Bar#getI()

What I know is possible:

我所知道的是可能的:

Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);

What I want to know is if there is a way to verify that a Barwith particular things true about it was passed as an argument.

我想知道的是,是否有一种方法可以验证Bar具有特定内容的a 是否作为参数传递。

采纳答案by Niccolò

In Mockito 2.1.0 and up with Java 8 you can pass the lambda to argThat out of the boxso that one does not need a custom argument matchers. For the example in the OP would be:

Mockito 2.1.0 及更高版本的 Java 8 中,您可以开箱用地将 lambda 传递给 argThat,这样就不需要自定义参数匹配器了。对于 OP 中的示例,将是:

verify(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));

This is because as of Mockito 2.1.0, ArgumentMatcheris a functional interface.

这是因为从 Mockito 2.1.0 开始,它ArgumentMatcher是一个功能接口。

回答by Captain Man

If you are using Mockito 2.1.0 or above and Java 8 or above, see thisanswer instead, it's much simpler now.

如果您使用的是 Mockito 2.1.0 或更高版本和 Java 8 或更高版本,请参阅答案,它现在简单得多。



I found the answer while writing the question.

我在写问题时找到了答案。

Yes, you can. Instead of using any(Bar.class)you'll need to implement your own instance of ArgumentMatcher<T>and use Mockito#argThat(Matcher), for example, say we want to check that iis 5...

是的你可以。any(Bar.class)您需要实现自己的ArgumentMatcher<T>and use实例而不是 using Mockito#argThat(Matcher),例如,假设我们要检查i5 ...

// in the test (could also be outside)

private static final class BarIs5 extends ArgumentMatcher<Bar> {

  @Override
  public boolean matches(Object argument) {
    return ((Bar) argument).getI() == 5;
  }
}

Then verify like so: verify(mockedFoo).doThing(argThat(new BarIs5()));

然后像这样验证: verify(mockedFoo).doThing(argThat(new BarIs5()));



Kick it up a notch by adding constructor parameters!

通过添加构造函数参数将其提升一个档次!

private static final class BarIsWhat extends ArgumentMatcher<Bar> {

  private final int i;

  public BarIsWhat(int i) {
    this.i = i
  }

  @Override
  public boolean matches(Object argument) {
    return ((Bar) argument).getI() == i;
  }
}

Then verify like so: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));

然后像这样验证: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));



Update:This popped in my queue because of a badge and saw some room for improvement.

更新:由于徽章,这突然出现在我的队列中,并看到了一些改进的空间。

I have tried this and it works. You can sort of use a lambda expression which is a lot cleaner (if you don't mind unchecked cast warnings at least).

我试过这个,它的工作原理。您可以使用更清晰的 lambda 表达式(如果您至少不介意未经检查的强制转换警告)。

The only issue is that argThataccepts a Hamcrest Matcherwhich is nota @FunctionalInterface. Luckily, Mockito's ArgumentMatcheris an abstract class that extends it and only has a single abstract method.

唯一的问题是,argThat接受HamcrestMatcher这是不是一个@FunctionalInterface。幸运的是,MockitoArgumentMatcher是一个抽象类,它扩展了它并且只有一个抽象方法。

In your test (or some common location) make a method like below

在您的测试(或一些常见位置)中制作如下方法

private static <T> ArgumentMatcher<T> matches(Predicate<T> predicate) {
  return new ArgumentMatcher<T>() {

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object argument) {
      return predicate.test((T) argument);
    }
  };
}

Now, in your test you can do this to use a lambda expression:

现在,在您的测试中,您可以使用 lambda 表达式执行此操作:

verify(mockedFoo).doThing(argThat(matches( (Bar arg) -> arg.getI() == 5 )));

回答by yuranos

If using Mockito 2+ is not an option, you can also use good old ArgumentCaptor. It'll be a bit more verbose though:

如果使用 Mockito 2+ 不是一种选择,您也可以使用 good old ArgumentCaptor. 不过,它会更冗长:

ArgumentCaptor<Long> siteIdCaptor = ArgumentCaptor.forClass(Long.class);
verify(repository).findBySiteId(siteIdCaptor.capture());
assertEquals(15, siteIdCaptor.getValue().longValue());