Vérifier la valeur d'attribut d'objet avec mockito

264

J'ai un appel de méthode dont je veux me moquer avec mockito. Pour commencer, j'ai créé et injecté une instance d'un objet sur lequel la méthode sera appelée. Mon objectif est de vérifier l'un des objets dans l'appel de méthode.

Existe-t-il un moyen que mockito vous permet d'affirmer ou de vérifier l'objet et ses attributs lorsque la méthode mock est appelée?

exemple

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

Au lieu de faire, anyObject()je veux vérifier que cet argument contient des champs particuliers

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Priyank
la source
Au lieu d'utiliser mockito dans ces cas, vous pouvez envisager de créer un stub personnalisé qui étend la classe de mockedObject et remplace certainsMethodOnMockedObject pour enregistrer l'objet pour une comparaison ultérieure.
Gonen I

Réponses:

540

La nouvelle fonctionnalité ajoutée à Mockito rend cela encore plus facile,

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

Jetez un œil à la documentation Mockito


Dans le cas où il existe plusieurs paramètres et que la capture d'un seul paramètre est souhaitée, utilisez d'autres ArgumentMatchers pour encapsuler le reste des arguments:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
iraSenthil
la source
1
si votre méthode a plus d'un argument, vous devez également utiliser Matchers pour tous les autres arguments. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa
1
Et s'il y a plusieurs arguments? Comment spécifiez-vous exactement celui qui vous intéresse?
IgorGanapolsky
2
@IgorGanapolsky En supposant un deuxième paramètre String pour doSomething, vous devez faire: vérifier (mock) .doSomething (argument.capture (), anyString ());
GreenTurtle
la nécessité d'utiliser des appariements pour tous les arguments est uniquement conforme aux spécifications d'utilisation du matcher tout ou rien standard.
Charney Kaye
54

Je pense que la façon la plus simple de vérifier un objet argument est d'utiliser la refEqméthode:

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

Il peut être utilisé même si l'objet n'est pas implémenté equals(), car la réflexion est utilisée. Si vous ne souhaitez pas comparer certains champs, ajoutez simplement leurs noms comme arguments pour refEq.

John29
la source
1
c'est une manière très élégante mais malheureusement org.mockito.Matchers est désormais obsolète
ihebiheb
5
@ihebiheb Déplacé vers ArgumentMatchers
Michael
48

Une autre possibilité, si vous ne souhaitez pas utiliser ArgumentCaptor(par exemple, parce que vous utilisez également le stubbing), est d'utiliser Hamcrest Matchers en combinaison avec Mockito.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
charleyc
la source
2
Sidenote: assurez-vous que le Matcherspackage est correct, car l'écriture de la même ligne de code avec la org.mockito.Matchersclasse lève une exception trompeuse indiquant que le paramètre de la fonction de simulation ne correspond tout simplement pas.
buer
1
Veuillez noter que dans les versions modernes de Mockito, c'est MockitoHamcrest.argThat()et nonMockito.argThat()
Roman Puchkovskiy
17

Cette réponse est basée sur la réponse de iraSenthil mais avec une annotation ( Captor ). À mon avis, cela présente certains avantages:

  • c'est plus court
  • c'est plus facile à lire
  • il peut gérer des génériques sans avertissements

Exemple:

@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"));
    }
}
Walery Strauch
la source
Cela ne fonctionnera que pour un seul argument dans les paramètres.
IgorGanapolsky
Vous pouvez utiliser un capteur pour plusieurs arguments. Si vous capturez plusieurs arguments, vous pouvez lister tous les résultats avec captor.getAllValues(). La méthode captor.getValue()utilisée dans la réponse fournit le dernier résultat.
Walery Strauch, le
11

Si vous utilisez Java 8, vous pouvez utiliser des expressions Lambda pour correspondre.

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

Exemple d'appel

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

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

Plus d'infos: http://source.coveo.com/2014/10/01/java8-mockito/

GuiSim
la source
5

Les solutions ci-dessus n'ont pas vraiment fonctionné dans mon cas. Je ne pouvais pas utiliser ArgumentCaptor car la méthode était appelée plusieurs fois et j'avais besoin de valider chacune. Un simple Matcher avec "argThat" a fait l'affaire facilement.

Matcher personnalisé

// 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)));
siffler
la source
3

Et une solution très agréable et propre en koltin de com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})
Cililing
la source
1

Vous pouvez consulter les éléments suivants:

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

Cela vérifiera si la méthode de mockedObject est appelée avec le paramètre desireObject comme paramètre.

zaid bepari
la source
1

Un autre moyen simple de le faire:

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
la source
0

Le javadoc pour refEq a mentionné que le contrôle d'égalité est superficiel! Vous pouvez trouver plus de détails sur le lien ci-dessous:

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

Le problème "d'égalité superficielle" ne peut pas être contrôlé lorsque vous utilisez d'autres classes qui n'implémentent pas la méthode .equals (), la classe "DefaultMongoTypeMapper" est un exemple où la méthode .equals () n'est pas implémentée.

org.springframework.beans.factory.support propose une méthode qui peut générer une définition de bean au lieu de créer une instance de l'objet, et elle peut être utilisée pour supprimer les échecs de comparaison.

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

** "La définition du bean n'est qu'une description du bean, pas un bean lui-même. Les descriptions du bean implémentent correctement equals () et hashCode (), donc plutôt que de créer un nouveau DefaultMongoTypeMapper () nous fournissons une définition qui indique au printemps comment il devrait en créer un "

Dans votre exemple, vous pouvez faire quelque chose comme ça

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());
Saif Masadeh
la source
0

Une solution simplifiée, sans créer de nouvelle classe d'implémentation Matcher et sans utiliser l'expression lambda:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
murali mohan
la source