简体   繁体   English

用 mockito 验证 object 属性值

[英]Verify object attribute value with mockito

I have a method call which I want to mock with mockito. To start with I have created and injected an instance of an object on which the method will be called.我有一个方法调用,我想用 mockito 模拟。首先,我创建并注入了一个 object 的实例,将在该实例上调用该方法。 My aim is to verify one of the object in method call.我的目标是验证方法调用中的 object 之一。

Is there a way that mockito allows you to assert or verify the object and it's attributes when the mock method is called?有没有一种方法 mockito 允许您在调用模拟方法时断言或验证 object 及其属性?

example例子

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>anyObject())

Instead of doing anyObject() i want to check that argument object contains some particular fields我不想做anyObject()我想检查参数 object 是否包含一些特定的字段

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)

New feature added to Mockito makes this even easier,添加到 Mockito 的新功能使这变得更加容易,

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Take a look at Mockito documentation查看 Mockito 文档


In case when there are more than one parameters, and capturing of only single param is desired, use other ArgumentMatchers to wrap the rest of the arguments:如果有多个参数,并且只需要捕获单个参数,请使用其他 ArgumentMatcher 包装其余参数:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());

I think the easiest way for verifying an argument object is to use the refEq method:我认为验证参数对象的最简单方法是使用refEq方法:

Mockito.verify(mockedObject).someMethodOnMockedObject(ArgumentMatchers.refEq(objectToCompareWith));

It can be used even if the object doesn't implement equals() , because reflection is used.即使对象没有实现equals()也可以使用它,因为使用了反射。 If you don't want to compare some fields, just add their names as arguments for refEq .如果您不想比较某些字段,只需将它们的名称添加为refEq参数。

One more possibility, if you don't want to use ArgumentCaptor (for example, because you're also using stubbing), is to use Hamcrest Matchers in combination with Mockito.另一种可能性,如果您不想使用ArgumentCaptor (例如,因为您也在使用存根),则将 Hamcrest Matchers 与 Mockito 结合使用。

import org.mockito.Mockito
import org.hamcrest.Matchers
...

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));

This is answer based on answer from iraSenthil but with annotation ( Captor ).这是基于iraSenthil 的答案但带有注释( Captor )的答案 In my opinion it has some advantages:在我看来,它有一些优点:

  • it's shorter它更短
  • it's easier to read更容易阅读
  • it can handle generics without warnings它可以在没有警告的情况下处理泛型

Example:例子:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest{

    @Captor
    private ArgumentCaptor<List<SomeType>> captor;

    //...

    @Test 
    public void shouldTestArgsVals() {
        //...
        verify(mockedObject).someMethodOnMockedObject(captor.capture());

        assertThat(captor.getValue().getXXX(), is("expected"));
    }
}

If you're using Java 8, you can use Lambda expressions to match.如果您使用的是 Java 8,则可以使用 Lambda 表达式进行匹配。

import java.util.Optional;
import java.util.function.Predicate;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

public class LambdaMatcher<T> extends BaseMatcher<T>
{
    private final Predicate<T> matcher;
    private final Optional<String> description;

    public LambdaMatcher(Predicate<T> matcher)
    {
        this(matcher, null);
    }

    public LambdaMatcher(Predicate<T> matcher, String description)
    {
        this.matcher = matcher;
        this.description = Optional.ofNullable(description);
    }

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

    @Override
    public void describeTo(Description description)
    {
        this.description.ifPresent(description::appendText);
    }
}

Example call示例调用

@Test
public void canFindEmployee()
{
    Employee employee = new Employee("John");
    company.addEmployee(employee);

    verify(mockedDal).registerEmployee(argThat(new LambdaMatcher<>(e -> e.getName()
                                                                         .equals(employee.getName()))));
}

More info: http://source.coveo.com/2014/10/01/java8-mockito/更多信息: http : //source.coveo.com/2014/10/01/java8-mockito/

A simplified solution, without creating a new Matcher implementation class and using lambda expression:一种简化的解决方案,无需创建新的 Matcher 实现类和使用 lambda 表达式:

verify(mockObject).someMockMethod(
        argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue)));

The solutions above didn't really work in my case.上面的解决方案在我的情况下并没有真正起作用。 I couldn't use ArgumentCaptor as the method was called several times and I needed to validate each one.我无法使用 ArgumentCaptor,因为该方法被多次调用,我需要验证每一个。 A simple Matcher with "argThat" did the trick easily.一个简单的带有“argThat”的 Matcher 很容易做到这一点。

Custom Matcher自定义匹配器

// custom matcher
private class PolygonMatcher extends ArgumentMatcher<PolygonOptions> {
    private int fillColor;
    public PolygonMatcher(int fillColor) {
        this.fillColor = fillColor;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof PolygonOptions)) return false;
        PolygonOptions arg = (PolygonOptions)argument;
        return Color.red(arg.getFillColor()) == Color.red(fillColor)
                && Color.green(arg.getFillColor()) == Color.green(fillColor)
                && Color.blue(arg.getFillColor()) == Color.blue(fillColor);
    }
}

Test Runner测试运行器

// do setup work setup
// 3 light green polygons
int green = getContext().getResources().getColor(R.color.dmb_rx_bucket1);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(green)));

// 1 medium yellow polygons
int yellow = getContext().getResources().getColor(R.color.dmb_rx_bucket4);
    verify(map, times(1)).addPolygon(argThat(new PolygonMatcher(yellow)));

// 3 red polygons
int orange = getContext().getResources().getColor(R.color.dmb_rx_bucket5);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(orange)));

// 2 red polygons
int red = getContext().getResources().getColor(R.color.dmb_rx_bucket7);
verify(map, times(2)).addPolygon(argThat(new PolygonMatcher(red)));

And very nice and clean solution in koltin from com.nhaarman.mockito_kotlin来自com.nhaarman.mockito_kotlin非常漂亮和干净的解决方案

verify(mock).execute(argThat {
    this.param = expected
})

You can refer the following:您可以参考以下内容:

Mockito.verify(mockedObject).someMethodOnMockedObject(eq(desiredObject))

This will verify whether method of mockedObject is called with desiredObject as parameter.这将验证是否使用 desiredObject 作为参数调用了 mockedObject 的方法。

The javadoc for refEq mentioned that the equality check is shallow! refEq 的 javadoc 提到相等性检查很浅! You can find more details at the link below:您可以在以下链接中找到更多详细信息:

https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq(T,%20java.lang.String...) https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq(T,%20java.lang.String...)

"shallow equality" issue cannot be controlled when you use other classes which don't implement .equals() method,"DefaultMongoTypeMapper" class is an example where .equals() method is not implemented.当您使用其他未实现 .equals() 方法的类时,无法控制“浅相等”问题,“DefaultMongoTypeMapper”类是未实现 .equals() 方法的示例。

org.springframework.beans.factory.support offers a method that can generate a bean definition instead of creating an instance of the object, and it can be used to git rid of Comparison Failure. org.springframework.beans.factory.support 提供了一种可以生成 bean 定义而不是创建对象实例的方法,它可以用来消除比较失败。

 genericBeanDefinition(DefaultMongoTypeMapper.class)
                        .setScope(SCOPE_SINGLETON)
                        .setAutowireMode(AUTOWIRE_CONSTRUCTOR)
                        .setLazyInit(false)
                        .addConstructorArgValue(null)
                        .getBeanDefinition()

**"The bean definition is only a description of the bean, not a bean itself. the bean descriptions properly implement equals() and hashCode(), so rather than creating a new DefaultMongoTypeMapper() we provide a definition that tells spring how it should create one" **“bean 定义只是对 bean 的描述,而不是 bean 本身。bean 描述正确地实现了 equals() 和 hashCode(),因此与其创建新的 DefaultMongoTypeMapper(),我们提供了一个定义来告诉 spring 它如何应该创造一个”

In your example, you can do somethong like this在你的例子中,你可以做这样的事情

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());

Another easy way to do so:另一种简单的方法是:

import org.mockito.BDDMockito;    
import static org.mockito.Matchers.argThat;
import org.mockito.ArgumentMatcher;

BDDMockito.verify(mockedObject)
        .someMethodOnMockedObject(argThat(new ArgumentMatcher<TypeOfMethodArg>() {

            @Override
            public boolean matches(Object argument) {
                final TypeOfMethodArg castedArg = (TypeOfMethodArg) argument;

                // Make your verifications and return a boolean to say if it matches or not
                boolean isArgMarching = true;

                return isArgMarching;
            }
        }));

I've been working in a setup where we have instantized mocked service classes, with methods.我一直在使用方法实例化模拟服务类的设置中工作。 And if I want to verify that a method has been called exactly 1 time, with an object parameter that contains a specific value in a field, here is how I would do it, for class Service , with method updateMethod(UpdateObject updateObjectParam){} :如果我想验证一个方法是否被调用了 1 次,参数 object 在字段中包含特定值,对于class Service ,我会这样做,方法updateMethod(UpdateObject updateObjectParam){} :

@Mock
Service service;

...

Mockito.verify(service, times(1)).updateMethod(argThat(updateParamObject -> updateParamObject.getField().equals("fieldValue")));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM