Mockito peut-il utiliser une méthode sans tenir compte de l'argument?

302

J'essaie de tester un code hérité, en utilisant Mockito.

Je veux écraser un FooDaoqui est utilisé dans la production comme suit:

foo = fooDao.getBar(new Bazoo());

Je peux écrire:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Mais le problème évident est qu'il getBar()n'est jamais appelé avec le même Bazooobjet que celui pour lequel j'ai stoppé la méthode. (Maudissez cet newopérateur!)

Je serais ravi si je pouvais écraser la méthode de manière à ce qu'elle renvoie myFooquel que soit l'argument. À défaut, j'écouterai d'autres suggestions de contournement, mais j'aimerais vraiment éviter de changer le code de production jusqu'à ce qu'il y ait une couverture de test raisonnable.

Eric Wilson
la source

Réponses:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

ou (pour éviter nulls):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

N'oubliez pas d'importer des matchers (beaucoup d'autres sont disponibles):

Pour Mockito 2.1.0 et plus récent:

import static org.mockito.ArgumentMatchers.*;

Pour les anciennes versions:

import static org.mockito.Matchers.*;
Tomasz Nurkiewicz
la source
2
J'adore quand la réponse précède la fin du «gel de la réponse d'acceptation».
Eric Wilson
10
Il y en a un notNull(Bazoo.class)juste comme any(Bazoo.class)(peut-être qu'il n'existait pas au moment de cette réponse)
Dandre Allison
2
j'ai eu une situation légèrement spéciale où je pouvais avoir l'un ou l'autre des deux arguments possibles - Bazooou Cazooqui sont tous les deux des sous-classes de, disons Azoo. car Bazooj'avais besoin de revenir foo, mais Cazooj'avais besoin de revenir bar. dans cette situation, la Matchers.any()solution proposée ne fonctionne pas, cependant, Matchers.isA()fonctionne parfaitement.
Tanvir
3
org.mockito.Matchersest désormais obsolète - utilisez-le à la org.mockito.ArgumentMatchersplace, c'est-à-dire import static org.mockito.ArgumentMatchers.*(voir la documentation )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6rchid
18

Utilisez comme ceci:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Avant d'importer Mockito.Matchers

Hamad
la source
1
C'est déprécié!
DrB
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() devrait répondre à vos besoins.

En outre, vous pouvez toujours envisager d'implémenter hashCode()et equals()pour la Bazooclasse. Cela ferait fonctionner votre exemple de code comme vous le souhaitez.

Buhb
la source
D'accord avec la deuxième suggestion, mais je continue de choisir de ne pas le faire pour des raisons non techniques.
Eric Wilson
1
La classe Matchers est déconseillée (voir la documentation - "Cette classe sera probablement supprimée dans la version 3.0" )
Johannes Rabauer
1

Une autre option consiste à s'appuyer sur une bonne equalsméthode à l' ancienne . Tant que l'argument dans la whenmaquette est equalsl'argument dans le code testé, alors Mockito correspondra à la maquette.

Voici un exemple.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

puis, en supposant que vous savez quelle sera la valeur someField, vous pouvez vous en moquer comme ceci.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

avantages: C'est plus explicite que les anymatchers. En tant que réviseur de code, je garde un œil ouvert anydans l'écriture de code pour les développeurs juniors, car il jette un coup d'œil sur la logique de leur code pour générer l'objet approprié transmis.

con: Parfois, le champ transmis à l'objet est un ID aléatoire. Dans ce cas, vous ne pouvez pas facilement construire l'objet argument attendu dans votre code maquette.

Une autre approche possible consiste à utiliser l' Answerobjet de Mockito qui peut être utilisé avec la whenméthode. Answervous permet d'intercepter l'appel réel, d'inspecter l'argument d'entrée et de renvoyer un objet factice. Dans l'exemple ci-dessous, j'utilise anypour intercepter toute demande concernant la méthode à moquer. Mais ensuite, dans le Answerlambda, je peux inspecter davantage l'argument Bazo ... peut-être pour vérifier qu'une ID appropriée lui a été transmise. Je préfère cela anypar lui-même afin qu'au moins une certaine inspection soit faite sur l'argument.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Donc, pour résumer le tout, j'aime me baser sur equals(où l'argument attendu et l'argument réel doivent être égaux) et si l'égalité n'est pas possible (en raison de ne pas pouvoir prédire l'état de l'argument réel), je vais recourir pour Answerinspecter l'argument.

Jose Martinez
la source