Comment résoudre l'exception de stubbing inutile

102

Mon code est comme ci-dessous,

@RunWith(MockitoJUnitRunner.class)
public class MyClass {

    private static final String code ="Test";

    @Mock
     private MyClassDAO dao;

    @InjectMocks
     private MyClassService Service = new MyClassServiceImpl();

    @Test
     public void testDoSearch() throws Exception {
         final String METHOD_NAME = logger.getName().concat(".testDoSearchEcRcfInspections()");
         CriteriaDTO dto = new CriteriaDTO();
         dto.setCode(code);
         inspectionService.searchEcRcfInspections(dto);
         List<SearchCriteriaDTO> summaryList = new ArrayList<SearchCriteriaDTO>();
         inspectionsSummaryList.add(dto);
         when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
         verify(dao).doSearchInspections(dto);

      }
}

Je suis en dessous de l'exception

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected in test class: Test
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
  at org.mockito.internal.exceptions.Reporter.formatUnncessaryStubbingException(Reporter.java:838)
  at org.mockito.internal.junit.UnnecessaryStubbingsReporter.validateUnusedStubs(UnnecessaryStubbingsReporter.java:34)
  at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:49)
  at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:103)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

S'il vous plaît, aidez-moi à résoudre

VHS
la source

Réponses:

119

Remplacez @RunWith(MockitoJUnitRunner.class)par @RunWith(MockitoJUnitRunner.Silent.class).

Sumit
la source
44
Bienvenue. il vaudrait la peine de mettre à jour votre réponse pour expliquer pourquoi ils OP devraient remplacer un tel code. Cela les aidera ainsi que les futurs visiteurs à comprendre.
Bugs
5
Btw, it is @RunWith(MockitoJUnitRunner.Silent.class)and not SILENT
fgysin reinstate Monica
6
À Kotlin:@RunWith(MockitoJUnitRunner.Silent::class)
Juan Saravia
10
Je ne sais pas pourquoi cette réponse continue de susciter des votes sans explication. D'autres réponses sont plus significatives et précises.
Yogesh
9
Cela ne résout pas le problème, mais supprime simplement le message d'erreur et affectera également tous les autres tests (le cas échéant) de la classe.
Escrimeur
100

Au début, vous devez vérifier votre logique de test. Il y a généralement 3 cas. Tout d'abord, vous vous moquez d'une mauvaise méthode (vous avez fait une faute de frappe ou quelqu'un a changé le code testé pour que la méthode simulée ne soit plus utilisée). Deuxièmement, votre test échoue avant que cette méthode ne soit appelée. Troisièmement, votre logique tombe dans une mauvaise branche if / switch quelque part dans le code, de sorte que la méthode simulée n'est pas appelée.

Si c'est le premier cas, vous voulez toujours changer la méthode fictive pour celle utilisée dans le code. Avec le deuxième et le troisième cela dépend. Habituellement, vous devez simplement supprimer cette maquette si elle ne sert à rien. Mais parfois, il y a certains cas dans les tests paramétrés, qui devraient emprunter ce chemin différent ou échouer plus tôt. Ensuite, vous pouvez diviser ce test en deux ou plusieurs tests distincts, mais ce n'est pas toujours beau. 3 méthodes de test avec éventuellement 3 fournisseurs d'arguments peuvent vous donner l'impression que le test est illisible. Dans ce cas pour JUnit 4, vous mettez cette exception en silence avec soit

@RunWith(MockitoJUnitRunner.Silent.class) 

annotation ou si vous utilisez l'approche par règles

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);

ou (le même comportement)

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

Pour les tests JUnit 5, vous pouvez désactiver cette exception à l'aide de l'annotation fournie dans le mockito-junit-jupiterpackage.

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}
Dcortez
la source
3
@MockitoSettings (strictness = Strictness.LENIENT) est le moyen le plus simple d'ajuster la rigueur dans ma configuration. Merci!
Matt
5
Cette réponse donne un bon aperçu des possibilités. Cependant, vous pouvez également définir la rigueur indulgente au cas par cas en utilisant Mockito.lenient().when(...); pour cette question particulière ce seraitMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus
Définissez ExtendWith dans la superclasse et MockitoSettings dans les sous-classes, lors du traitement des hiérarchies de test. J'espère que cela fera gagner du temps à quelqu'un à mes frais.
miracle_the_V
34

Le silence n'est pas une solution. Vous devez corriger votre maquette dans votre test. Voir la documentation officielle ici .

Les stubs inutiles sont des appels de méthode stub qui n'ont jamais été réalisés lors de l'exécution du test (voir aussi MockitoHint), exemple:

//code under test:
 ...
 String result = translator.translate("one")
 ...

 //test:
 ...
 when(translator.translate("one")).thenReturn("jeden"); // <- stubbing realized during code execution
 when(translator.translate("two")).thenReturn("dwa"); // <- stubbing never realized
 ...

Notez que l'une des méthodes stubbed n'a jamais été réalisée dans le code sous test, lors de l'exécution du test. Le stubbing errant peut être une erreur du développeur, l'artefact du copier-coller ou l'effet de ne pas comprendre le test / code. Dans tous les cas, le développeur se retrouve avec un code de test inutile. Afin de garder la base de code propre et maintenable, il est nécessaire de supprimer le code inutile. Sinon, les tests sont plus difficiles à lire et à raisonner.

Pour en savoir plus sur la détection des stubbings inutilisés, consultez MockitoHint.

Stéphane GRILLON
la source
13
Il existe de nombreuses situations dans lesquelles vous écrivez 8 à 9 tests par rapport à une configuration @BeforeEach similaire dans laquelle l'élément retourné d'un stub est inutilisé en raison de la logique métier sur une poignée de tests. Vous pouvez soit (A) le diviser en plusieurs tests et copier / coller efficacement la section \ @BeforeEach moins le seul élément (B) Copier / coller la seule ligne sur laquelle Mockito est emo aux 6 tests qui l'utilisent et l'avoir pas dans les 2 qui ne le font pas ou (C) Utilisez silencieux. Je préfère utiliser silent / warn. Ce n'est pas un test cassé.
RockMeetHardplace
1
@RockMeetHardplace, Silent n'est pas une solution , rapidement vous voyez moins de copier / coller mais lors de la maintenance de vos tests par de nouvelles personnes sur votre projet ce sera problématique. Si la librairie Mockito fait ça, ce n'est pas pour rien.
Stéphane GRILLON
2
@sgrillon: Mais ce système détecte des tas de faux positifs. Autrement dit, cela indique que quelque chose n'est pas utilisé, mais ce n'est clairement pas le cas, car la suppression du stub interrompt l'exécution. Ce n'est pas que le code de test ne peut pas être amélioré, c'est qu'une ligne vitale de stubbing ne devrait jamais être détectée comme "inutile". D'où l'importance de pouvoir désactiver cette vérification, c'est trop pressé.
Carighan
@Carighan, si votre simulation est détectée comme incorrecte, ce n'est peut-être pas ce que vous pensez. Cela vous donne un test OK alors qu'il peut y avoir un bogue.
Stéphane GRILLON
@sgrillon, désolé de ne jamais vous avoir répondu. Il s'avère qu'il y avait un bogue avec ceci où, selon l'ordre d'exécution des tests, cela générait des "faux hits", où les stubs qui étaient utilisés dans un test mais écrasés dans un autre le déclencheraient. C'est fixe depuis longtemps, autant que je sache.
Carighan
29

Pour moi, @Ruleni les @RunWith(MockitoJUnitRunner.Silent.class)suggestions ni les suggestions n'ont fonctionné. C'était un projet hérité où nous avons mis à niveau vers mockito-core 2.23.0.

Nous pourrions nous en débarrasser UnnecessaryStubbingExceptionen utilisant:

Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());

au lieu de:

when(mockedService.getUserById(any())).thenReturn(new User());

Inutile de dire que vous devriez plutôt regarder le code de test, mais nous devions tout d'abord faire compiler le contenu et exécuter les tests;)

philonant
la source
7
A MON HUMBLE AVIS. C'est la réponse la plus utile que j'ai trouvée ici en mettant au silence toute la classe de test.
priyeshdkr
Puisque je voulais supprimer seulement 1 moquerie, c'est la meilleure réponse pour moi. Pas vraiment une réponse pour l'OP.
Hans Wouters
25
 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

L' whenici configure votre maquette pour faire quelque chose. Cependant, vous n'utilisez plus ce simulacre de quelque manière que ce soit après cette ligne (à part faire a verify). Mockito vous prévient que la whenligne est donc inutile. Peut-être avez-vous fait une erreur de logique?

john16384
la source
Merci pour votre aide
VHS
J'ai besoin des deux quand et vérifier les déclarations suggèrent gentiment comment aller plus loin
VHS
2
Appelez une fonction sur votre classe de test ( Service) pour voir si elle réagit correctement. Vous ne l'avez pas fait du tout, alors que testez-vous ici?
john16384
3

En regardant une partie de votre trace de pile, il semble que vous stubez l' dao.doSearch()ailleurs. Plus comme créer à plusieurs reprises les stubs de la même méthode.

Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.

Considérez la classe de test ci-dessous par exemple:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
    @Mock
    Service1 svc1Mock1;

    @Mock
    Service2 svc2Mock2;

    @InjectMock
    TestClass class;

    //Assume you have many dependencies and you want to set up all the stubs 
    //in one place assuming that all your tests need these stubs.

    //I know that any initialization code for the test can/should be in a 
    //@Before method. Lets assume there is another method just to create 
    //your stubs.

    public void setUpRequiredStubs() {
        when(svc1Mock1.someMethod(any(), any())).thenReturn(something));
        when(svc2Mock2.someOtherMethod(any())).thenReturn(somethingElse);
    }

    @Test
    public void methodUnderTest_StateUnderTest_ExpectedBehavior() {
        // You forget that you defined the stub for svcMock1.someMethod or 
        //thought you could redefine it. Well you cannot. That's going to be 
        //a problem and would throw your UnnecessaryStubbingException.
       when(svc1Mock1.someMethod(any(),any())).thenReturn(anyThing);//ERROR!
       setUpRequiredStubs();
    }
}

Je préfère envisager de refactoriser vos tests en stub si nécessaire.

railomaya
la source
1

Si vous utilisez ce style à la place:

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

remplacez-le par:

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
Greg
la source
1

J'avais UnnecessaryStubbingExceptionquand j'ai essayé d'utiliser les whenméthodes sur un objet Spy. Mockito.lenient()a fait taire l'exception mais les résultats des tests n'étaient pas corrects.

Dans le cas des objets Spy, il faut appeler les méthodes directement.

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
class ArithmTest {

    @Spy
    private Arithm arithm;

    @Test
    void testAddition() {

        int res = arithm.add(2, 5);

        // doReturn(7).when(arithm).add(2, 5);
        assertEquals(res, 7);
    }
}
Jan Bodnar
la source
1

Eh bien, dans mon cas, l'erreur Mockito me disait d'appeler la méthode réelle après le whenouwhenever stub . Comme nous n'invoquions pas les conditions dont nous venons de nous moquer, Mockito signalait cela comme des stubs ou du code inutiles.

Voici ce que c'était quand l'erreur arrivait:

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
}

alors je viens d'appeler la méthode réelle mentionnée dans l'instruction when pour se moquer de la méthode.

les changements effectués sont comme ci-dessous stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
    //called the actual method here
    stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)
}

ça marche maintenant.

Vikas Kumar
la source
1

Remplacer

@RunWith(MockitoJUnitRunner.class)

avec

@RunWith(MockitoJUnitRunner.Silent.class)

ou supprimer@RunWith(MockitoJUnitRunner.class)

ou tout simplement commenter les appels moqueurs indésirables (représentés comme des stubbing non autorisés).

Nakul Goyal
la source
0

Dans le cas d'un grand projet, il est difficile de corriger chacune de ces exceptions. Dans le même temps, l'utilisation Silentn'est pas conseillée. J'ai écrit un script pour supprimer tous les stubbings inutiles à partir d'une liste d'entre eux.

https://gist.github.com/cueo/da1ca49e92679ac49f808c7ef594e75b

Nous avons juste besoin de copier-coller la mvnsortie et d'écrire la liste de ces exceptions à l'aide de regex et de laisser le script s'occuper du reste.

mohitmayank
la source
-1

Si vous utilisez any () lorsque vous vous moquez, vous devez réespacer @RunWith (MockitoJUnitRunner.class) avec @RunWith (MockitoJUnitRunner.Silent.class).

Abdou ASSOUMANE
la source
C'est tout simplement faux. any()fonctionne parfaitement avec le coureur ordinaire lorsqu'il est utilisé correctement.
Matthew Lu il y a
-1

Lorsque vous créez une maquette et que cette maquette n'est pas utilisée, elle lève une exception de stubbing inutilisée. Dans votre cas, cette simulation n'est pas réellement appelée. Par conséquent, il jette cette erreur. Par conséquent, relpace @RunWith(MockitoJUnitRunner.class)avec @RunWith(MockitoJUnitRunner.Silent.class)lequel supprimerait l'erreur. Si vous souhaitez toujours utiliser, @RunWith(MockitoJUnitRunner.class)essayez de déboguer votre logique si la fonction que vous avez moquée est effectivement appelée ou non.

Shivam Kohli
la source
Cela n'ajoute rien qui n'a pas été couvert dans les réponses existantes.
Matthew Lu il y a