331

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. My aim is to verify one of the object in method call.

Is there a way that mockito allows you to assert or verify the object and it's attributes when the mock method is called?

example

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

Instead of doing anyObject() i want to check that argument object contains some particular fields

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Vadzim
  • 24,954
  • 11
  • 143
  • 151
Priyank
  • 14,231
  • 18
  • 78
  • 107
  • As an alternative to using mockito in these cases, you can consider creating a custom stub that extends the mockedObject's class, and overrides someMethodOnMockedObject to save the object for later comparison. – Gonen I Apr 06 '19 at 12:23

12 Answers12

661

New feature added to Mockito makes this even easier,

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

Take a look at Mockito documentation


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:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
Akash
  • 3
  • 3
iraSenthil
  • 11,307
  • 6
  • 39
  • 49
  • 2
    if your method has more than one argument, you must use Matchers for all others arguments too. https://akcasoy.wordpress.com/tag/argumentcaptor/ – robsonrosa Aug 05 '15 at 17:07
  • 1
    What if there are multiple arguments? How you specify the exact one you are interested in? – IgorGanapolsky Feb 24 '17 at 14:25
  • 2
    @IgorGanapolsky Assuming a second String parameter for doSomething you need to do: verify(mock).doSomething(argument.capture(), anyString()); – GreenTurtle Apr 07 '17 at 11:23
  • the need to use matchers for all arguments is solely per standard all-or-none matcher usage spec. – Charney Kaye Dec 01 '17 at 19:27
85

I think the easiest way for verifying an argument object is to use the refEq method:

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

It can be used even if the object doesn't implement equals(), because reflection is used. If you don't want to compare some fields, just add their names as arguments for refEq.

Datz
  • 3,156
  • 3
  • 22
  • 50
John29
  • 3,340
  • 3
  • 32
  • 50
  • 1
    that's a very elegant way but unfortunately org.mockito.Matchers is now deprecated – ihebiheb Feb 14 '19 at 15:15
  • 11
    @ihebiheb It's moved to ArgumentMatchers – Michael May 01 '19 at 11:48
  • Not seeing how this remotely answer the question of comparing fields: "I want to check that argument object contains some particular fields" – oligofren Sep 08 '21 at 10:43
  • 2
    This solution is useful _especially_ when you need to match arguments that do not implement `equals()`. I was trying to find a question/answer specifically about that usecase but didn’t find one, strangely. Yet I’m hesitant to post a self-answered question about this since I’m pretty sure there must be a duplicate somewhere… – Didier L Jan 06 '22 at 18:42
  • 2
    @oligofren `refEq` will check all fields through reflection, so it will cover the “_particular fields_” automatically. Moreover, if you do not need all of them, you can exclude some. The only cease it does not really cover is when your whitelist is shorter than your blacklist, e.g., you want only to test a few fields among many. – Didier L Jan 06 '22 at 18:45
  • Good comments. Might as well just update and expand this answer. – oligofren Jan 07 '22 at 06:58
56

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.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
Alexi Courieux
  • 92
  • 2
  • 11
charleyc
  • 1,699
  • 12
  • 10
  • 5
    Sidenote: make sure the `Matchers` package is correct, as writing the same line of code with the `org.mockito.Matchers` class throws a misleading exception stating that the mock function's parameter simply doesn't match. – buer Jan 20 '18 at 19:36
  • 5
    Please note that in modern Mockito versions, it is `MockitoHamcrest.argThat()` and not `Mockito.argThat()` – Roman Puchkovskiy Jul 17 '19 at 14:19
21

This is answer based on answer from iraSenthil but with annotation (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"));
    }
}
Community
  • 1
  • 1
Walery Strauch
  • 6,792
  • 8
  • 50
  • 57
  • This will only work for a single argument in params. – IgorGanapolsky Feb 24 '17 at 14:29
  • You can use one captor for more than one argument. If you capture more than one argument you can list of all results with `captor.getAllValues()`. The method `captor.getValue()` that is used in answer delivers last result. – Walery Strauch Feb 24 '17 at 14:42
18

A simplified solution, without creating a new Matcher implementation class and using lambda expression:

verify(mockObject).someMockMethod(
        argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue)));
murali mohan
  • 211
  • 2
  • 3
11

If you're using Java 8, you can use Lambda expressions to match.

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/

GuiSim
  • 7,361
  • 6
  • 40
  • 50
7

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. A simple Matcher with "argThat" did the trick easily.

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)));
whizzle
  • 2,053
  • 17
  • 21
3

And very nice and clean solution in koltin from com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})
Cililing
  • 4,303
  • 1
  • 17
  • 35
2

You can refer the following:

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

This will verify whether method of mockedObject is called with desiredObject as parameter.

2

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){} :

@Mock
Service service;

...

Mockito.verify(service, times(1)).updateMethod(argThat(updateParamObject -> updateParamObject.getField().equals("fieldValue")));
jumps4fun
  • 3,994
  • 10
  • 50
  • 96
1

The javadoc for refEq mentioned that the equality check is shallow! 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...)

"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.

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.

 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"

In your example, you can do somethong like this

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());
Noumenon
  • 5,099
  • 4
  • 53
  • 73
Arar
  • 1,926
  • 5
  • 29
  • 47
1

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;
            }
        }));
pierrefevrier
  • 1,570
  • 4
  • 22
  • 33