使用mockito验证对象属性值

Pri*_*ank 243 java mocking mockito

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

有没有一种方法可以让mockito在调用mock方法时断言或验证对象及其属性?

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>anyObject())
Run Code Online (Sandbox Code Playgroud)

而不是anyObject()我想检查该参数对象包含一些特定的字段

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Run Code Online (Sandbox Code Playgroud)

ira*_*hil 498

Mockito新增的功能使这更容易,

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

看看Mockito 文档

  • 如果你的方法有多个参数,那么你也必须对所有其他参数使用匹配器。https://akcasoy.wordpress.com/tag/argumentcaptor/ (2认同)
  • @IgorGanapolsky假设你需要做doSomething的第二个String参数:verify(mock).doSomething(argument.capture(),anyString()); (2认同)

Joh*_*n29 54

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

Mockito.verify(mockedObject).someMethodOnMockedObject(Matchers.refEq(objectToCompareWith));
Run Code Online (Sandbox Code Playgroud)

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

  • @ihebiheb已移至ArgumentMatchers (4认同)
  • 当您需要匹配未实现 equals() 的参数时,此解决方案非常有用。我试图找到一个专门关于该用例的问题/答案,但奇怪的是没有找到。然而,我很犹豫是否要发布一个关于此问题的自我回答的问题,因为我很确定某个地方一定有重复的问题...... (2认同)
  • @oligofren `refEq` 将通过反射检查所有字段,因此它将自动覆盖“_特定字段_”。此外,如果您不需要全部,您可以排除一些。它没有真正涵盖的唯一停止是当您的白名单比黑名单短时,例如,您只想测试许多字段中的几个字段。 (2认同)

cha*_*eyc 45

ArgumentCaptor另一种可能性,如果你不想使用(例如,因为你也使用了存根),就是将Hamcrest Matchers与Mockito结合使用.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(Mockito.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
Run Code Online (Sandbox Code Playgroud)

  • 旁注:确保`Matchers` 包是正确的,因为使用`org.mockito.Matchers` 类编写同一行代码会抛出一个误导性异常,指出模拟函数的参数根本不匹配。 (3认同)
  • 请注意,在现代 Mockito 版本中,它是 `MockitoHamcrest.argThat()` 而不是 `Mockito.argThat()` (3认同)

Wal*_*uch 16

这是基于来自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"));
    }
}
Run Code Online (Sandbox Code Playgroud)


Gui*_*Sim 9

如果您使用的是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

示例电话

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

    verify(mockedDal).registerEmployee(argThat(new LambdaMatcher<>(e -> e.getName()
                                                                         .equals(employee.getName()))));
}
Run Code Online (Sandbox Code Playgroud)

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


mur*_*han 7

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

verify(mockObject).someMockMethod(
        argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue)));
Run Code Online (Sandbox Code Playgroud)


whi*_*zle 6

上面的解决方案在我的情况下并没有真正起作用。我无法使用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试运行器

// 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)));
Run Code Online (Sandbox Code Playgroud)