使用Mockito验证对象属性值


264

我有一个方法调用,我想用模拟嘲笑。首先,我创建并注入了将在其上调用该方法的对象的实例。我的目的是验证方法调用中的对象之一。

调用模拟方法时,mockito有没有办法让您断言或验证对象及其属性?

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

anyObject()我不想检查参数对象是否包含某些特定字段,而不是这样做

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

在这种情况下,可以使用替代方法来代替使用模拟,可以考虑创建一个自定义存根,以扩展模拟对象的类,并覆盖someMethodOnMockedObject以保存对象以供以后比较。
Gonen I

Answers:


539

Mockito中新增的功能使此操作变得更加容易,

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

看看Mockito 文档


如果有多个参数,并且只需要捕获单个参数,请使用其他ArgumentMatchers来包装其余参数:

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

1
如果您的方法有多个自变量,则还必须对所有其他自变量使用Matchers。akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa

1
如果有多个参数怎么办?如何指定您感兴趣的确切对象?
IgorGanapolsky

2
@IgorGanapolsky假设您需要执行doSomething的第二个String参数:verify(mock).doSomething(argument.capture(),anyString());
GreenTurtle

对于所有参数使用匹配器的需求完全是根据标准的全有或无匹配器使用规范。
Charney Kaye

54

我认为验证参数对象的最简单方法是使用refEq方法:

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

即使对象未实现equals(),也可以使用它,因为使用了反射。如果您不想比较某些字段,只需将其名称添加为的参数refEq


1
这是一种非常优雅的方法,但不幸的是org.mockito.Matchers现在已被弃用
ihebiheb

5
@ihebiheb它已移至ArgumentMatchers
Michael

48

如果您不想使用ArgumentCaptor(例如,因为您还在使用存根),另一种可能性是将Hamcrest Matchers与Mockito结合使用。

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

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

2
旁注:请确保Matchers程序包正确,因为使用org.mockito.Matchers该类编写同一行代码会引发误导性异常,指出模拟函数的参数根本不匹配。
布尔

1
请注意,在现代版本的Mockito,这是MockitoHamcrest.argThat()Mockito.argThat()
罗马Puchkovskiy

17

这是基于iraSenthil的答案的答案,但带有批注(Captor)。我认为它具有一些优点:

  • 它更短
  • 更容易阅读
  • 它可以在没有警告的情况下处理泛型

例:

@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"));
    }
}

这仅适用于params中的单个参数。
IgorGanapolsky

您可以将一个捕获器用于多个参数。如果捕获多个参数,则可以使用列出所有结果captor.getAllValues()captor.getValue()答案中使用的方法 将传递最后的结果。
Walery Strauch

11

如果您使用的是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);
    }
}

呼叫范例

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

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

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


5

在我的情况下,上述解决方案并没有真正起作用。我不能使用ArgumentCaptor,因为该方法已被调用多次,因此我需要验证每个方法。一个带有“ argThat”的简单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);
    }
}

测试亚军

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

3

而且来自 com.nhaarman.mockito_kotlin

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

1

您可以参考以下内容:

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

这将验证是否以所需对象作为参数调用了嘲笑对象的方法。


1

另一种简便的方法是:

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;
            }
        }));

0

refEq的Javadoc提到相等检查很浅!您可以在下面的链接中找到更多详细信息:

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

当您使用其他未实现.equals()方法的类时,“浅平等”问题无法控制,“ DefaultMongoTypeMapper”类是未实现.equals()方法的示例。

org.springframework.beans.factory.support提供了一种可以生成bean定义而不是创建对象实例的方法,并且该方法可用于避免比较失败。

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

**“ bean定义仅是bean的描述,而不是bean本身。bean的描述正确地实现了equals()和hashCode(),因此,我们提供了一个告诉Spring的定义,而不是创建一个新的DefaultMongoTypeMapper()。应该创造一个

在您的示例中,您可以执行以下操作

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

0

一种简化的解决方案,无需创建新的Matcher实现类并使用lambda表达式:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.