Les matchers Mockito sont des méthodes statiques et des appels à ces méthodes, qui remplacent les arguments lors des appels à when
et verify
.
Les matchers Hamcrest (version archivée) (ou matchers de style Hamcrest) sont des instances d'objet sans état à usage général qui implémentent Matcher<T>
et exposent une méthode matches(T)
qui renvoie true si l'objet correspond aux critères du Matcher. Ils sont destinés à être exempts d'effets secondaires et sont généralement utilisés dans des affirmations telles que celle ci-dessous.
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Les correspondants Mockito existent, séparés des correspondants de style Hamcrest, de sorte que les descriptions des expressions correspondantes s'intègrent directement dans les appels de méthode : les correspondants Mockito retournent là où les méthodes de mise en correspondance Hamcrest renvoient des objets Matcher T
(de type Matcher<T>
).
Matchers Mockito sont invoquées par des méthodes statiques telles que eq
, any
, gt
et startsWith
sur org.mockito.Matchers
et org.mockito.AdditionalMatchers
. Il existe également des adaptateurs, qui ont changé d'une version à l'autre de Mockito:
- Pour Mockito 1.x,
Matchers
certains appels (tels que intThat
ou argThat
) sont des correspondants Mockito qui acceptent directement les correspondants Hamcrest comme paramètres. ArgumentMatcher<T>
élargie org.hamcrest.Matcher<T>
, qui a été utilisé dans la représentation interne et Hamcrest était une classe de base de matcher Hamcrest à la place de toute sorte de Mockito matcher.
- Pour Mockito 2.0+, Mockito n'a plus de dépendance directe sur Hamcrest.
Matchers
les appels formulés en tant qu'objets enveloppants intThat
ou qui ne sont plus implémentés mais qui sont utilisés de manière similaire. Les adaptateurs Hamcrest tels que et sont toujours disponibles, mais ont été déplacés vers .argThat
ArgumentMatcher<T>
org.hamcrest.Matcher<T>
argThat
intThat
MockitoHamcrest
Que les matchers soient de type Hamcrest ou simplement de style Hamcrest, ils peuvent être adaptés comme suit:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
Dans la déclaration ci-dessus: foo.setPowerLevel
est une méthode qui accepte un int
. is(greaterThan(9000))
renvoie a Matcher<Integer>
, qui ne fonctionnerait pas comme setPowerLevel
argument. Le matcher Mockito intThat
encapsule ce Matcher de style Hamcrest et renvoie un int
pour qu'il puisse apparaître comme un argument; Les correspondances Mockito comme gt(9000)
envelopperaient toute cette expression en un seul appel, comme dans la première ligne de l'exemple de code.
Ce que les matchers font / retournent
when(foo.quux(3, 5)).thenReturn(true);
Lorsque vous n'utilisez pas de correspondance d'argument, Mockito enregistre vos valeurs d'argument et les compare avec leurs equals
méthodes.
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
Lorsque vous appelez une correspondance comme any
ou gt
(supérieure à), Mockito stocke un objet de correspondance qui oblige Mockito à ignorer cette vérification d'égalité et à appliquer la correspondance de votre choix. Dans ce cas, argumentCaptor.capture()
il stocke un matcher qui enregistre son argument à la place pour une inspection ultérieure.
Les correspondants renvoient des valeurs factices telles que zéro, des collections vides ou null
. Mockito essaie de renvoyer une valeur fictive sûre et appropriée, comme 0 pour anyInt()
ou any(Integer.class)
ou une valeur vide List<String>
pour anyListOf(String.class)
. En raison de l'effacement de type, cependant, Mockito n'a pas d'informations de type pour renvoyer n'importe quelle valeur sauf null
pour any()
ou argThat(...)
, ce qui peut provoquer une exception NullPointerException si vous essayez de "décompresser automatiquement" une null
valeur primitive.
Les correspondants aiment eq
et gt
prennent des valeurs de paramètres; idéalement, ces valeurs devraient être calculées avant le début du stubbing / vérification. Appeler un simulacre en se moquant d'un autre appel peut interférer avec le stubbing.
Les méthodes Matcher ne peuvent pas être utilisées comme valeurs de retour; il n'y a aucun moyen d' exprimer thenReturn(anyInt())
ou thenReturn(any(Foo.class))
de Mockito, par exemple. Mockito a besoin de savoir exactement quelle instance retourner dans les appels de stubbing et ne choisira pas une valeur de retour arbitraire pour vous.
Détails d'implémentation
Les matchers sont stockés (en tant que matchers d'objets de style Hamcrest) dans une pile contenue dans une classe appelée ArgumentMatcherStorage . MockitoCore et Matchers possèdent chacun une instance ThreadSafeMockingProgress , qui contient statiquement un ThreadLocal contenant des instances MockingProgress. C'est ce MockingProgressImpl qui contient un ArgumentMatcherStorageImpl concret . Par conséquent, l'état de simulation et de correspondance est statique mais à la portée des threads de manière cohérente entre les classes Mockito et Matchers.
La plupart avec une exception pour les matchers comme les appels matcher ne font qu'ajouter à cette pile, and
, or
etnot
. Cela correspond parfaitement à (et repose sur) l' ordre d'évaluation de Java , qui évalue les arguments de gauche à droite avant d'appeler une méthode:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
Cette volonté:
- Ajoutez
anyInt()
à la pile.
- Ajoutez
gt(10)
à la pile.
- Ajoutez
lt(20)
à la pile.
- Retirez
gt(10)
et lt(20)
et ajouter and(gt(10), lt(20))
.
- Call
foo.quux(0, 0)
, qui (sauf stubbed autrement) renvoie la valeur par défaut false
. En interne, Mockito marque quux(int, int)
l'appel le plus récent.
- Call
when(false)
, qui rejette son argument et prépare la méthode stub quux(int, int)
identifiée en 5. Les deux seuls états valides sont avec la longueur de pile 0 (égalité) ou 2 (matchers), et il y a deux matchers sur la pile (étapes 1 et 4), donc Mockito stubs la méthode avec un any()
matcher pour son premier argument et and(gt(10), lt(20))
pour son deuxième argument et efface la pile.
Cela démontre quelques règles:
Mockito ne peut pas faire la différence entre quux(anyInt(), 0)
et quux(0, anyInt())
. Ils ressemblent tous les deux à un appel à quux(0, 0)
avec un matcher int sur la pile. Par conséquent, si vous utilisez un matcher, vous devez faire correspondre tous les arguments.
L'ordre des appels n'est pas seulement important, c'est ce qui fait que tout fonctionne . L'extraction de correspondances vers des variables ne fonctionne généralement pas, car elle modifie généralement l'ordre des appels. Cependant, l'extraction des correspondances aux méthodes fonctionne très bien.
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
La pile change assez souvent pour que Mockito ne puisse pas la contrôler très soigneusement. Il ne peut vérifier la pile que lorsque vous interagissez avec Mockito ou un simulacre, et doit accepter des correspondants sans savoir s'ils sont utilisés immédiatement ou abandonnés accidentellement. En théorie, la pile devrait toujours être vide en dehors d'un appel à when
ou verify
, mais Mockito ne peut pas le vérifier automatiquement. Vous pouvez vérifier manuellement avec Mockito.validateMockitoUsage()
.
Dans un appel à when
, Mockito appelle en fait la méthode en question, qui lèvera une exception si vous avez stubblé la méthode pour lever une exception (ou si vous avez besoin de valeurs non nulles ou non nulles).
doReturn
et doAnswer
(etc.) n'invoquent pas la méthode réelle et sont souvent une alternative utile.
Si vous aviez appelé une méthode fictive au milieu du stubbing (par exemple pour calculer une réponse pour un eq
matcher), Mockito vérifierait la longueur de pile par rapport à cet appel à la place, et échouerait probablement.
Si vous essayez de faire quelque chose de mal, comme le stubbing / vérifier une méthode finale , Mockito appellera la méthode réelle et laissera également des correspondants supplémentaires sur la pile . L' final
appel de méthode ne peut pas lever d' exception, mais vous pouvez obtenir une InvalidUseOfMatchersException à partir des correspondances parasites lors de votre prochaine interaction avec une simulation.
Problèmes communs
InvalidUseOfMatchersException :
Vérifiez que chaque argument a exactement un appel de correspondance, si vous utilisez des correspondances, et que vous n'avez pas utilisé de correspondance en dehors d'un appel when
ou verify
. Les correspondants ne doivent jamais être utilisés comme valeurs de retour ou champs / variables stubbed.
Vérifiez que vous n'appelez pas une simulation dans le cadre de la fourniture d'un argument de correspondance.
Vérifiez que vous n'essayez pas de stub / vérifier une méthode finale avec un matcher. C'est un excellent moyen de laisser un matcher sur la pile, et à moins que votre méthode finale ne lève une exception, c'est peut-être la seule fois où vous réalisez que la méthode que vous vous moquez est définitive.
NullPointerException avec arguments primitifs: (Integer) any()
renvoie null tandis que any(Integer.class)
retourne 0; cela peut provoquer un NullPointerException
si vous attendez un int
au lieu d'un entier. Dans tous les cas, préférez anyInt()
, qui renverra zéro et sautera également l'étape d'auto-boxing.
NullPointerException ou d' autres exceptions: appels à when(foo.bar(any())).thenReturn(baz)
se fait appeler foo.bar(null)
, que vous pourriez avoir bouchonné à jeter une exception lors de la réception d' un argument nul. Le passage à doReturn(baz).when(foo).bar(any())
ignore le comportement stubbed .
Dépannage général
Utilisez MockitoJUnitRunner , ou appelez explicitement validateMockitoUsage
votre méthode tearDown
ou @After
(ce que le coureur ferait pour vous automatiquement). Cela aidera à déterminer si vous avez mal utilisé les correspondants.
À des fins de débogage, ajoutez validateMockitoUsage
directement des appels à dans votre code. Cela lancera si vous avez quelque chose sur la pile, ce qui est un bon avertissement d'un mauvais symptôme.
Juste un petit ajout à l'excellente réponse de Jeff Bowman, car j'ai trouvé cette question en cherchant une solution à l'un de mes propres problèmes:
Si un appel à une méthode correspond à plusieurs
when
appels entraînés simulés , l'ordre deswhen
appels est important et doit être du plus large au plus spécifique. À partir de l'un des exemples de Jeff:est l'ordre qui garantit le résultat (probablement) souhaité:
Si vous inversez les appels quand, le résultat sera toujours
true
.la source