Come risolvere un'eccezione di stubbing non necessaria


101

Il mio codice è come di seguito,

@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);

      }
}

Sto ottenendo sotto l'eccezione

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)

Per favore aiutami a risolvere

Risposte:


120

Sostituisci @RunWith(MockitoJUnitRunner.class)con @RunWith(MockitoJUnitRunner.Silent.class).


44
Benvenuto. varrebbe la pena aggiornare la tua risposta per spiegare perché OP dovrebbe sostituire tale codice. Questo aiuterà loro e i futuri visitatori a capire.
Bug

5
A proposito, è @RunWith(MockitoJUnitRunner.Silent.class)e non SILENZIOSO
fgysin ripristina Monica il

6
A Kotlin:@RunWith(MockitoJUnitRunner.Silent::class)
Juan Saravia

9
Non sono sicuro del motivo per cui questa risposta continua a ricevere voti positivi senza una spiegazione. Altre risposte sono più significative e accurate.
Yogesh

8
Ciò non risolve il problema, ma elimina semplicemente il messaggio di errore e influirà anche su tutti gli altri test (se presenti) nella classe.
Schermidore

98

All'inizio dovresti controllare la logica del test. Di solito ci sono 3 casi. Innanzitutto, stai prendendo in giro il metodo sbagliato (hai fatto un errore di battitura o qualcuno ha cambiato il codice testato in modo che il metodo deriso non venga più utilizzato). Secondo, il tuo test fallisce prima che questo metodo venga chiamato. Terzo, la tua logica cade nel ramo if / switch sbagliato da qualche parte nel codice in modo che il metodo deriso non venga chiamato.

Se questo è il primo caso, vuoi sempre cambiare il metodo deriso per quello usato nel codice. Con il secondo e il terzo dipende. Di solito dovresti semplicemente eliminare questo mock se non serve. Ma a volte ci sono alcuni casi nei test parametrizzati, che dovrebbero prendere questo percorso diverso o fallire prima. Quindi puoi dividere questo test in due o più test separati, ma non è sempre bello. 3 metodi di test con possibilmente 3 provider di argomenti possono farti sembrare illeggibile. In tal caso per JUnit 4 silenzia questa eccezione con entrambi

@RunWith(MockitoJUnitRunner.Silent.class) 

annotazione o se stai usando l'approccio delle regole

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

o (lo stesso comportamento)

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

Per i test JUnit 5 è possibile silenziare questa eccezione utilizzando l'annotazione fornita nel mockito-junit-jupiterpacchetto.

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}

3
@MockitoSettings (strictness = Strictness.LENIENT) è il modo più semplice per modificare il rigore nella mia configurazione. Grazie!
Matt

4
Questa risposta fornisce una buona panoramica delle possibilità. Tuttavia, puoi anche impostare il rigore indulgente caso per caso utilizzando Mockito.lenient().when(...); per questa particolare domanda sarebbeMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus

Definisci ExtendWith nella superclasse e MockitoSettings nelle sottoclassi, quando si tratta di gerarchie di test. Spero che questo faccia risparmiare tempo a qualcuno a mie spese.
miracle_the_V

34

Il silenzio non è una soluzione. Devi correggere la tua simulazione nel test. Consulta la documentazione ufficiale qui .

Gli stub non necessari sono chiamate di metodo stubbed che non sono mai state realizzate durante l'esecuzione del test (vedere anche MockitoHint), esempio:

//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
 ...

Si noti che uno dei metodi bloccati non è mai stato realizzato nel codice sottoposto a test, durante l'esecuzione del test. Lo stubbing vagante potrebbe essere una svista dello sviluppatore, l'artefatto del copia-incolla o l'effetto non comprendere il test / codice. In ogni caso, lo sviluppatore si ritrova con un codice di test non necessario. Per mantenere la base di codice pulita e gestibile è necessario rimuovere il codice non necessario. Altrimenti i test sono più difficili da leggere e ragionare.

Per saperne di più sul rilevamento di stubbing inutilizzati, vedere MockitoHint.


13
Ci sono molte situazioni in cui scrivi 8-9 test su una configurazione @BeforeEach simile in cui l'elemento restituito da uno stub è inutilizzato a causa della logica aziendale su una manciata di test. Puoi (A) suddividerlo in più test e copiare / incollare efficacemente la sezione \ @BeforeEach meno l'elemento (B) Copiare / incollare la singola riga che Mockito sta facendo nei 6 test che la usano e ce l'hanno non nei 2 che non lo fanno o (C) Usa il silenzio. Preferisco usare silent / warn. Non è un test rotto.
RockMeetHardplace

1
@RockMeetHardplace, Silent non è una soluzione , rapidamente vedrai meno copia / incolla ma quando manterrai i tuoi test da nuove persone sul tuo progetto questo sarà problematico. Se la libreria Mockito lo fa non è per niente.
Stéphane GRILLON

2
@grillon: Ma questo sistema rileva un sacco di falsi positivi. Cioè, dice che qualcosa è inutilizzato, ma chiaramente non lo è, poiché la rimozione dello stub interrompe l'esecuzione. Non è che il codice di test non possa essere migliorato, è che una linea vitale di stubbing non dovrebbe mai essere rilevata come "non necessaria". Da qui l'importanza di poter disabilitare questo controllo, è troppo ansioso.
Carighan

@Carighan, se la tua simulazione viene rilevata come errata, potrebbe non essere quello che pensi. Questo ti dà un test OK mentre potrebbe esserci un bug.
Stéphane GRILLON

@grillon, scusa se non ti ho mai risposto. Si scopre che c'era un bug con questo in cui, a seconda dell'ordine di esecuzione del test, generava "falsi risultati", dove gli stub che erano stati usati in un test ma sovrascritti in un altro lo avrebbero attivato. Tuttavia è stato risolto da tempo, per quanto ne so.
Carighan

27

Per me né il @Rulené i @RunWith(MockitoJUnitRunner.Silent.class)suggerimenti hanno funzionato. Era un progetto legacy in cui siamo passati a mockito-core 2.23.0.

Potremmo sbarazzarci di UnnecessaryStubbingExceptionusando:

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

invece di:

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

Inutile dire che dovresti piuttosto guardare il codice del test, ma prima di tutto dovevamo compilare il materiale e eseguire i test;)


6
A PARER MIO. Questa è la risposta più utile che ho trovato qui invece di mettere a tacere l'intera classe di test.
priyeshdkr

Dato che volevo sopprimere solo una presa in giro, questa è la migliore risposta per me. Non è proprio una risposta per l'OP.
Hans Wouters,

25
 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

Il whenqui configura il tuo finto di fare qualcosa. Tuttavia, non usi più questo mock in alcun modo dopo questa riga (a parte fare a verify). Mockito ti avverte che la whenlinea quindi è inutile. Forse hai commesso un errore logico?


Grazie per il vostro aiuto
VHS

Ho bisogno di entrambi i tempi e le dichiarazioni di verifica suggeriscono gentilmente come andare oltre
VHS

2
Chiama una funzione sulla tua classe di test ( Service) per vedere se reagisce correttamente. Non l'hai fatto affatto, quindi cosa stai testando qui?
john16384

3

Guardando una parte della tua traccia dello stack sembra che tu stia stubbing dao.doSearch()altrove. Più come creare ripetutamente gli stub dello stesso metodo.

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.

Considera la seguente classe di test ad esempio:

@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();
    }
}

Preferirei considerare di refactoring i tuoi test per stub dove necessario.


2

Se invece stai usando questo stile:

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

sostituiscilo con:

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

2

Sostituire

@RunWith(MockitoJUnitRunner.class)

con

@RunWith(MockitoJUnitRunner.Silent.class)

o rimuovere@RunWith(MockitoJUnitRunner.class)

o semplicemente commentare le chiamate beffarde indesiderate (mostrate come stubbing non autorizzato).


1

L'ho fatto UnnecessaryStubbingExceptionquando ho provato a utilizzare i whenmetodi su un oggetto Spy. Mockito.lenient()ha messo a tacere l'eccezione ma i risultati del test non erano corretti.

In caso di oggetti Spy, è necessario chiamare direttamente i metodi.

@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);
    }
}

1

Bene, nel mio caso l'errore di Mockito mi diceva di chiamare il metodo effettivo dopo lo stub wheno whenever. Poiché non stavamo invocando le condizioni che abbiamo appena preso in giro, Mockito le ha segnalate come stub o codice non necessari.

Ecco com'era quando si stava verificando l'errore:

@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)
}

quindi ho chiamato il metodo effettivo menzionato nell'istruzione when per deridere il metodo.

le modifiche apportate sono le seguenti 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)
}

sta lavorando adesso.


0

In caso di un progetto di grandi dimensioni, è difficile correggere ciascuna di queste eccezioni. Allo stesso tempo, l'utilizzo Silentnon è consigliato. Ho scritto uno script per rimuovere tutti gli stubbing non necessari dato un elenco di essi.

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

Dobbiamo solo copiare-incollare l' mvnoutput e scrivere l'elenco di queste eccezioni usando regex e lasciare che lo script si occupi del resto.


0

Se usi any () quando deridi, devi riposizionare @RunWith (MockitoJUnitRunner.class) con @RunWith (MockitoJUnitRunner.Silent.class).


0

Quando crei una simulazione e quella simulazione non viene utilizzata, genera un'eccezione di stubbing inutilizzata. Nel tuo caso quella finta non è effettivamente chiamata. Quindi lancia quell'errore. Quindi, relpace @RunWith(MockitoJUnitRunner.class)con il @RunWith(MockitoJUnitRunner.Silent.class)quale rimuoverebbe l'errore. Se vuoi ancora usare, @RunWith(MockitoJUnitRunner.class)prova a eseguire il debug della tua logica se la funzione che hai deriso viene effettivamente chiamata o meno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.