Mockito. Vérifier les arguments de méthode

220

J'ai googlé à ce sujet, mais je n'ai rien trouvé de pertinent. J'ai quelque chose comme ça:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

Maintenant, je veux vérifier que mymethod(Object o), qui est appelé à l'intérieur runtestmethod(), a été appelé avec l'objet o, pas avec un autre. Mais je passe toujours le test, quoi que je mette sur la vérification, par exemple, avec:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

ou

Mockito.verify(mock.mymethod(Mockito.eq(null)));

ou

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

Je passe toujours le test. Comment puis-je effectuer cette vérification (si possible)?

Je vous remercie.

manolowar
la source

Réponses:

334

Une alternative à ArgumentMatcheris ArgumentCaptor.

Exemple officiel:

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

Un capteur peut également être défini à l'aide de l' annotation @Captor :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
la source
1
Merci pour l'échantillon! Je ne l'ai jamais utilisé. Ça fait un peu bizarre d'avoir des choses comme captor dans le code, mais ça a aidé.
Artemis
1
Haha, je n'ai pas compris la question, mais la réponse m'a beaucoup aidé. Merci :-)
Marcus K.
13
Important: appelez verify () / capture () après avoir utilisé la maquette. Je pensais qu'il devait être "installé" avant ...
Daniel Alder
1
Merci pour cette réponse!
Jose Flavio Quispe Irrazábal
C'est une excellente réponse!! Merci beaucoup!
Ulky Igor
61

Essayez-vous de faire l'égalité logique en utilisant la méthode .equals de l'objet? Vous pouvez le faire en utilisant le matcher argThat inclus dans Mockito

import static org.mockito.Matchers.argThat

Ensuite, vous pouvez implémenter votre propre argument matcher qui se reportera à chaque objet.

private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
    T thisObject;

    public ObjectEqualityArgumentMatcher(T thisObject) {
        this.thisObject = thisObject;
    }

    @Override
    public boolean matches(Object argument) {
        return thisObject.equals(argument);
    }
}

Maintenant, en utilisant votre code, vous pouvez le mettre à jour pour lire ...

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj)).thenReturn(null);

Testeable obj = new Testeable();
obj.setMockeable(mock);
command.runtestmethod();

verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));

Si vous optez pour l'égalité EXACT (même objet en mémoire), faites simplement

verify(mock).mymethod(obj);

Cela vérifiera qu'il a été appelé une fois.

Matthew Kirkley
la source
1
Vous pouvez utiliser la construction en ReflectionEqualsclasse à cette fin.
takacsot
2
+1 pour votre réponse. Mais je voudrais ajouter que verify(mock).mymethod(obj);cela ne vérifie pas l'égalité EXACTE (même objet en mémoire). Au lieu de cela, il utilise la méthode des objets égaux qui aurait pu être remplacée.
efux
Vous pouvez également créer une implémentation anonyme ArgumentMatcherpour être moins verbeux.
botchniaque
1
Plus de détails: par défaut, verify()invoque la equals()méthode / de l'argument / entrant plutôt que la méthode / de l'objet / enregistré equals(). cela n'est pas pertinent, sauf si vous essayez de confirmer que votre sujet de test renvoie une instance d'objet spécifique et que le sujet renvoie ce qui est censé être un décorateur transparent de cette instance à la place. L' verifyargument equals()ne connaîtrait pas le décorateur; tandis que le décorateur equals()serait réécrit pour tolérer l'original. Dans ce cas, votre test échouera faussement.
Mark McKenna
54
  • Vous n'avez pas besoin du eqmatcher si vous n'utilisez pas d'autres matchers.
  • Vous n'utilisez pas la syntaxe correcte - votre appel de méthode doit être en dehors de .verify(mock). Vous lancez maintenant la vérification sur le résultat de l'appel de méthode, sans rien vérifier (ne pas faire d'appel de méthode). Par conséquent, tous les tests réussissent.

Votre code devrait ressembler à:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
la source
J'avais essayé ça avant, et encore maintenant pour être sûr. J'ai toujours le même problème, le test passe toujours.
manolowar
2
Il verifeis par référence
cnexans
17

argThat plus lambda

voici comment échouer la vérification de votre argument:

    verify(mock).mymethod(argThat(
      (x)->false
    ));

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

argThat plus affirme

le test ci-dessus "dira" Expected: lambda$... Was: YourClass.toSting.... Vous pouvez obtenir une cause plus spécifique de l'échec si vous utilisez des assertions dans le lambda:

    verify(mock).mymethod(argThat( x -> {
      assertThat(x).isNotNull();
      assertThat(x.description).contains("KEY");
      return true;
    }));

MAIS: CECI NE FONCTIONNE QUE AVEC 1 APPEL DE MÉTHODE. Si la méthode vérifiée est appelée au moins 2 fois, mockito transmet toutes les combinaisons appelées à chaque vérificateur. Donc, mockito s'attend à ce que votre vérificateur retourne silencieusement truepour l'un des ensembles d'arguments et false(sans exception d'affirmation) pour les autres appels valides. Cette attente n'est pas un problème pour 1 appel de méthode - elle devrait simplement renvoyer true 1 fois.

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

Maintenant , le test dit: Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. REMARQUE: j'ai utilisé des assertJassertions, mais c'est à vous de choisir le cadre d'assertion à utiliser.


argThat avec plusieurs arguments.

Si vous utilisez argThat, tous les arguments doivent être fournis avec des correspondances. Par exemple:

    verify(mock).mymethod(eq("VALUE_1"), argThat((x)->false));
    // above is correct as eq() is also an argument matcher.

verify(mock).mymethod("VALUE_1", argThat((x)->false));

// above is incorrect; an exceptoin will be thrown, as the fist arg. is given without an argument matcher.

où:

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

eq matcher

la façon la plus simple de vérifier si l'argument est égal:

verify(mock).mymethod(eq(expectedValue));
// NOTE:   ^ where the parentheses must be closed.

argument direct

si la comparaison par référence est acceptable, alors continuez avec:

verify(mock).mymethod(expectedArg);
// NOTE:   ^ where the parentheses must be closed.

LA CAUSE ROOT de l' échec de la question initiale était au mauvais endroit des paranthes: verify(mock.mymethod.... C'était faux. Le droit serait:verify(mock).*

épox
la source
1
Celui-ci est ma réponse préférée, fonctionne et beaucoup plus élégant que les autres.
Airwavezx
11

J'ai utilisé Mockito.verify de cette manière

@UnitTest
public class JUnitServiceTest
{
    @Mock
    private MyCustomService myCustomService;


    @Test
    public void testVerifyMethod()
    {
       Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
       Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
       Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
       Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
       Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
       Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
    }
}
Libre d'esprit
la source
5

Avez-vous vérifié la méthode equals pour la classe mockable? Si celui-ci retourne toujours vrai ou si vous testez la même instance par rapport à la même instance et que la méthode égale n'est pas remplacée (et ne vérifie donc que les références), alors elle retourne vrai.

rit
la source
4

L'autre méthode consiste à utiliser la méthode org.mockito.internal.matchers.Equals.Equals au lieu d'en redéfinir une:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Nils Renaud
la source
3

L'avez-vous essayé avec le même () matcher? Un péché:

verify(mockObj).someMethod(same(specificInstance));

J'ai eu le même problème. Je l'ai essayé avec le matcher eq () ainsi que le matcher refEq () mais j'ai toujours eu des faux positifs. Lorsque j'utilisais le même correspondeur (), le test échouait lorsque les arguments étaient des instances différentes et passait une fois que les arguments étaient la même instance.

cbbcloud
la source
-1

Vous pouvez également utiliser TypeSafeDiagnosingMatcher

    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {

        StringBuilder text = new StringBuilder(500);

        @Override
        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
            String productCode = req.getPackageIds().iterator().next().getValue();
            if (productCode.equals(request.getSupplierProductCode())) {
                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                return true;
            }

            text.append(req.toString());
            return false;
        }

        @Override
        public void describeTo(Description d) {
            d.appendText(text.toString());
        }
    };
}

Vérifiez ensuite cette invocation:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
la source