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.