Verifica il valore dell'attributo oggetto con mockito


264

Ho una chiamata di metodo che voglio prendere in giro con mockito. Per cominciare, ho creato e iniettato un'istanza di un oggetto su cui verrà chiamato il metodo. Il mio obiettivo è verificare uno degli oggetti nella chiamata del metodo.

C'è un modo in cui mockito ti consente di affermare o verificare l'oggetto e i suoi attributi quando viene chiamato il metodo mock?

esempio

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

Invece di farlo, anyObject()voglio verificare che l'oggetto argomento contenga alcuni campi particolari

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

In alternativa all'utilizzo di mockito in questi casi, puoi prendere in considerazione la creazione di uno stub personalizzato che estende la classe mockedObject e sovrascrive someMethodOnMockedObject per salvare l'oggetto per un successivo confronto.
— Gonen I,

Risposte:


540

La nuova funzionalità aggiunta a Mockito lo rende ancora più semplice,

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

Dai un'occhiata alla documentazione di Mockito


Nel caso in cui vi siano più di un parametro e si desideri catturare solo un singolo parametro, utilizzare altri ArgumentMatcher per racchiudere il resto degli argomenti:

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

1
se il tuo metodo ha più di un argomento, devi usare Matchers anche per tutti gli altri argomenti. akcasoy.wordpress.com/tag/argumentcaptor
— robsonrosa il

1
Cosa succede se ci sono più argomenti? Come specifichi quello esatto che ti interessa?
— IgorGanapolsky,

2
@IgorGanapolsky Supponendo un secondo parametro String per doSomething devi fare: verifica (finto) .doSomething (argomento.capture (), anyString ());
— GreenTurtle

la necessità di utilizzare i matcher per tutti gli argomenti è esclusivamente conforme alle specifiche di utilizzo dei matcher standard tutte o nessuna.
— Charney Kaye,

54

Penso che il modo più semplice per verificare un oggetto argomento sia usare il refEqmetodo:

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

Può essere utilizzato anche se l'oggetto non viene implementato equals(), poiché viene utilizzato il riflesso. Se non vuoi confrontare alcuni campi, aggiungi semplicemente i loro nomi come argomenti per refEq.


1
è un modo molto elegante ma sfortunatamente org.mockito.Matchers è ora deprecato
— ihebiheb

5
@ihebiheb È stato spostato su ArgumentMatchers
— Michael,

48

Un'altra possibilità, se non si desidera utilizzare ArgumentCaptor(ad esempio perché si utilizza anche la mozzata), è utilizzare Hamcrest Matcher in combinazione con Mockito.

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

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

2
Sidenote: assicurati che il Matcherspacchetto sia corretto, poiché scrivere la stessa riga di codice con la org.mockito.Matchersclasse genera un'eccezione fuorviante affermando che il parametro della funzione di simulazione semplicemente non corrisponde.
— acquista il

1
Si noti che nelle moderne versioni di Mockito, lo è MockitoHamcrest.argThat()e non èMockito.argThat()
— Roman Puchkovskiy,

17

Questa è una risposta basata sulla risposta di iraSenthil ma con annotazione ( Captor ). Secondo me ha alcuni vantaggi:

  • è più corto
  • è più facile da leggere
  • può gestire generici senza avvertimenti

Esempio:

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

Funzionerà solo per un singolo argomento in params.
— IgorGanapolsky,

Puoi usare un captor per più di un argomento. Se acquisisci più di un argomento, puoi elencare tutti i risultati con captor.getAllValues(). Il metodo captor.getValue()utilizzato nella risposta fornisce l'ultimo risultato.
— Walery Strauch,

11

Se stai usando Java 8, puoi usare espressioni Lambda per abbinare.

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

Chiamata di esempio

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

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

Maggiori informazioni: http://source.coveo.com/2014/10/01/java8-mockito/


5

Le soluzioni sopra non hanno funzionato nel mio caso. Non ho potuto usare ArgumentCaptor poiché il metodo è stato chiamato più volte e ho dovuto convalidare ciascuno di essi. Un semplice Matcher con "argThat" ha funzionato facilmente.

Matcher personalizzato

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

3

E soluzione molto bella e pulita in koltin da com.nhaarman.mockito_kotlin

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

1

È possibile fare riferimento a quanto segue:

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

Ciò verificherà se il metodo di mockedObject viene chiamato con desiderataObject come parametro.


1

Un altro modo semplice per farlo:

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

Il javadoc di refEq ha affermato che il controllo dell'uguaglianza è superficiale! Puoi trovare maggiori dettagli al link qui sotto:

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

Il problema "uguaglianza superficiale" non può essere controllato quando si utilizzano altre classi che non implementano il metodo .equals (), la classe "DefaultMongoTypeMapper" è un esempio in cui il metodo .equals () non è implementato.

org.springframework.beans.factory.support offre un metodo in grado di generare una definizione bean anziché creare un'istanza dell'oggetto e può essere utilizzato per eliminare l'errore di confronto.

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

** "La definizione del bean è solo una descrizione del bean, non un bean stesso. Le descrizioni del bean implementano correttamente equals () e hashCode (), quindi anziché creare un nuovo DefaultMongoTypeMapper () forniamo una definizione che dice a primavera come dovrebbe crearne uno "

Nel tuo esempio, puoi fare qualcosa del genere

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

0

Una soluzione semplificata, senza creare una nuova classe di implementazione Matcher e usando l'espressione lambda:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.