@RunWith (MockitoJUnitRunner.class) vs MockitoAnnotations.initMocks (ce)

118

Lors de l'écriture d'un nouveau test jUnit4, je me demande s'il faut utiliser @RunWith (MockitoJUnitRunner.class) ou MockitoAnnotations.initMocks (this) .

J'ai créé un nouveau test et l'assistant a généré automatiquement un test avec le Runner. Les Javadocs pour MockitoJUnitRunner indiquent ce qui suit:

Compatible avec JUnit 4.4 et supérieur, ce runner ajoute le comportement suivant:

Initialise les simulations annotées avec Mock, de sorte que l'utilisation explicite de MockitoAnnotations.initMocks (Object) n'est pas nécessaire. Les simulations sont initialisées avant chaque méthode de test. valide l'utilisation du framework après chaque méthode de test.

Je ne sais pas si l'utilisation du Runner a un avantage sur la méthode initMocks () que j'ai utilisée dans le passé.

Toutes les pensées ou liens seraient appréciés!

Océan bleu
la source

Réponses:

147

MockitoJUnitRunnervous donne une validation automatique de l'utilisation du framework, ainsi qu'un automatique initMocks().

La validation automatique de l'utilisation du framework en vaut la peine. Cela vous donne de meilleurs rapports si vous faites l'une de ces erreurs.

  • Vous appelez la whenméthode statique , mais ne terminez pas le stubbing avec une correspondance thenReturn, thenThrowou then. (Erreur 1 dans le code ci-dessous)

  • Vous appelez verifyun simulacre, mais oubliez de fournir l'appel de méthode que vous essayez de vérifier. (Erreur 2 dans le code ci-dessous)

  • Vous appelez la whenméthode après doReturn, doThrowou doAnsweret passez une maquette, mais oubliez de fournir la méthode que vous essayez de talon. (Erreur 3 dans le code ci-dessous)

Si vous n'avez pas de validation de l'utilisation du framework, ces erreurs ne sont pas signalées jusqu'à l' appel suivant à une méthode Mockito. Cela pourrait être

  • dans la même méthode de test (comme l'erreur 1 ci-dessous),
  • dans la méthode de test suivante (comme l'erreur 2 ci-dessous),
  • dans la classe de test suivante.

S'ils se produisent lors du dernier test que vous exécutez (comme l'erreur 3 ci-dessous), ils ne seront pas signalés du tout.

Voici à quoi pourrait ressembler chacun de ces types d'erreurs. Supposons ici que JUnit exécute ces tests dans l'ordre dans lequel ils sont répertoriés ici.

@Test
public void test1() {

    // ERROR 1
    // This compiles and runs, but it's an invalid use of the framework because 
    // Mockito is still waiting to find out what it should do when myMethod is called.
    // But Mockito can't report it yet, because the call to thenReturn might 
    // be yet to happen.
    when(myMock.method1());

    doSomeTestingStuff();

    // ERROR 1 is reported on the following line, even though it's not the line with
    // the error.
    verify(myMock).method2();

}

@Test
public void test2() {

    doSomeTestingStuff();

    // ERROR 2
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call to verify.  But Mockito can't report 
    // it yet, because the call to the method that's being verified might 
    // be yet to happen.
    verify(myMock);
}

@Test
public void test3() {

    // ERROR 2 is reported on the following line, even though it's not even in 
    // the same test as the error.
    doReturn("Hello").when(myMock).method1();


    // ERROR 3
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call is being stubbed.  But Mockito can't 
    // report it yet, because the call to the method that's being stubbed might 
    // be yet to happen.

    doReturn("World").when(myMock);

    doSomeTestingStuff(); 

    //  ERROR 3 is never reported, because there are no more Mockito calls. 
}

Maintenant, quand j'ai écrit cette réponse pour la première fois il y a plus de cinq ans, j'ai écrit

Je recommanderais donc l'utilisation du dans la MockitoJUnitRunnermesure du possible. Cependant, comme Tomasz Nurkiewicz l'a correctement souligné, vous ne pouvez pas l'utiliser si vous avez besoin d'un autre runner JUnit, tel que celui de Spring.

Ma recommandation a maintenant changé. L'équipe Mockito a ajouté une nouvelle fonctionnalité depuis que j'ai écrit cette réponse pour la première fois. C'est une règle JUnit, qui exécute exactement la même fonction que le MockitoJUnitRunner. Mais c'est mieux, car cela n'empêche pas l'utilisation d'autres coureurs.

Comprendre

@Rule 
public MockitoRule rule = MockitoJUnit.rule();

dans votre classe de test. Cela initialise les simulations et automatise la validation du framework; tout comme le MockitoJUnitRunnerfait. Mais maintenant, vous pouvez également utiliser SpringJUnit4ClassRunnerou tout autre JUnitRunner. À partir de Mockito 2.1.0, il existe des options supplémentaires qui contrôlent exactement le type de problème signalé.

Dawood ibn Kareem
la source
je ne peux certainement pas dire qu'ils sont pareils. dans un cas de test, la configuration de junit runner échoue pour moi et n'injecte pas mes mocks correctement sauf si je fais la configuration initMocks
dtc
Nous utilisons testng 6.8.8 + mockito 1.10.19 et, évidemment, nous ne pouvons pas utiliser MockitoJUnitRunner, mais le framework de validation fonctionne toujours! Et cela fonctionne exactement comme @David Wallace. Quelqu'un peut-il expliquer? Est-ce parce que nous avons encore des rappels @ Before * et MockitoAnnotations.initMocks (this)?
yuranos
@ yuranos87 Cela ressemble à quelque chose que vous devriez poser comme nouvelle question. N'oubliez pas d'inclure votre code lorsque vous le faites - il est un peu inutile de demander "pourquoi ce code fait-il XYZ" si vous ne l'affichez pas.
Dawood ibn Kareem
1
l'utilisation de la solution TestRunner surpasse de loin la @Rule
Règle du
1
@alexandroid Ma meilleure suggestion est que vous écriviez votre propre réponse en utilisant @ExtendWith. Ce n'est pas vraiment quelque chose que je connais. L'avantage de Stack Overflow est que sur une question comme celle-ci, vous pouvez vous retrouver avec plusieurs réponses correctes.
Dawood ibn Kareem
24

L'utilisation de runner vous permet d'économiser un peu de codage (pas besoin de @Beforeméthode). D'un autre côté, l'utilisation d'un runner n'est parfois pas possible, c'est-à-dire lorsque vous en utilisez déjà un, comme SpringJUnit4ClassRunner.

C'est tout. C'est juste une question de préférence.

Tomasz Nurkiewicz
la source
2
À part la ligne initMocks (), la méthode @Before serait toujours nécessaire pour toute autre configuration, non?
OceanBlue
2
@OceanBlue: Bien sûr, si votre @Beforeméthode contenait autre chose initMocks()que vous devez la conserver après la migration vers runner.
Tomasz Nurkiewicz
La réponse de David Wallace sur la validation du framework répond entièrement à ma question, donc j'ai accepté celle-là, mais +1 pour avoir souligné que ce coureur ne peut pas être utilisé avec un autre, comme le Spring. Merci!
OceanBlue
1
J'utilise Spring Boot et je peux dire que SpringJUnit4ClassRunner initialise automatiquement les simulations pour moi. Je ne sais pas pour le printemps ordinaire, cependant.
gustavohenke