Como resolver a exceção de stubbing desnecessário


100

Meu código é como abaixo,

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

      }
}

Estou ficando abaixo da exceção

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)

Por favor me ajude como resolver

Respostas:


120

Substitua @RunWith(MockitoJUnitRunner.class)por @RunWith(MockitoJUnitRunner.Silent.class).


44
Bem-vinda. valeria a pena atualizar sua resposta para explicar por que o OP deve substituir esse código. Isso os ajudará e os futuros visitantes a entender.
Bugs de

5
A propósito, é @RunWith(MockitoJUnitRunner.Silent.class)e não SILENCIOSO
fgysin reintegrar Monica em

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

9
Não sei por que essa resposta continua recebendo votos positivos sem uma explicação. Outras respostas são mais significativas e precisas.
Yogesh

8
Isso não resolve o problema, mas simplesmente suprime a mensagem de erro e também afetará todos os outros testes (se houver) da classe.
Fencer

97

Em primeiro lugar, você deve verificar sua lógica de teste. Normalmente são 3 casos. Primeiro, você está simulando um método errado (você cometeu um erro de digitação ou alguém alterou o código testado para que o método simulado não seja mais usado). Em segundo lugar, seu teste está falhando antes que este método seja chamado. Terceiro, sua lógica falha se / switch se ramificar em algum lugar no código, de forma que o método simulado não seja chamado.

Se este for o primeiro caso, você sempre deseja alterar o método simulado para aquele usado no código. Com o segundo e o terceiro, depende. Normalmente, você deve apenas excluir este mock se não tiver uso. Mas às vezes há certos casos em testes parametrizados, que devem seguir esse caminho diferente ou falhar antes. Em seguida, você pode dividir este teste em dois ou mais testes separados, mas nem sempre é bonito. 3 métodos de teste com possivelmente 3 provedores de argumentos podem fazer seu teste parecer ilegível. Nesse caso, para JUnit 4, você silencia essa exceção com qualquer

@RunWith(MockitoJUnitRunner.Silent.class) 

anotação ou se você estiver usando abordagem de regra

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

ou (o mesmo comportamento)

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

Para testes JUnit 5, você pode silenciar essa exceção usando a anotação fornecida no mockito-junit-jupiterpacote.

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

3
@MockitoSettings (strictness = Strictness.LENIENT) é a maneira mais simples de ajustar a rigidez em minha configuração. Obrigado!
Matt

4
Esta resposta fornece uma boa visão geral das possibilidades. No entanto, você também pode definir o rigor tolerante caso a caso usando Mockito.lenient().when(...); para esta questão em particular seriaMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus

Defina ExtendWith na superclasse e MockitoSettings nas subclasses, ao lidar com hierarquias de teste. Espero que isso economize tempo para alguém às minhas custas.
miracle_the_V

34

Silêncio não é solução. Você precisa corrigir seu mock em seu teste. Veja a documentação oficial aqui .

Stubs desnecessários são chamadas de método em stub que nunca foram realizadas durante a execução do teste (consulte também MockitoHint), exemplo:

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

Observe que um dos métodos de stub nunca foi realizado no código em teste, durante a execução do teste. O stub desgarrado pode ser uma omissão do desenvolvedor, o artefato de copiar e colar ou o efeito de não compreensão do teste / código. De qualquer forma, o desenvolvedor acaba com um código de teste desnecessário. Para manter a base de código limpa e sustentável, é necessário remover o código desnecessário. Caso contrário, os testes são mais difíceis de ler e raciocinar.

Para saber mais sobre como detectar stubbings não utilizados, consulte MockitoHint.


13
Existem muitas situações em que você escreve testes 8-9 em uma configuração @BeforeEach semelhante, em que o item retornado de um stub não é usado devido à lógica de negócios em um punhado de testes. Você pode (A) dividi-lo em vários testes e efetivamente copiar / colar a seção \ @BeforeEach menos um item (B) Copiar / colar a única linha que Mockito está prestes a fazer para os 6 testes que a usam e têm não no 2 que não ou (C) Use silêncio. Eu prefiro usar silencioso / avisar. Não é um teste quebrado.
RockMeetHardplace

1
@RockMeetHardplace, Silent não é uma solução , rapidamente você verá menos copiar / colar, mas ao manter seus testes por novas pessoas em seu projeto isso será problemático. Se a livraria Mockito faz isso, não é à toa.
Stéphane GRILLON

2
@sgrillon: Mas este sistema detecta muitos falsos positivos. Ou seja, diz que algo não é usado, mas claramente não é, pois a remoção do stub interrompe a execução. Não é que o código de teste não possa ser melhorado, é que uma linha vital de stub nunca deve ser detectada como "desnecessária". Daí a importância de poder desabilitar essa verificação, é muito ansioso.
Carighan

@Carighan, se sua simulação for detectada como incorreta, pode não ser o que você pensa. Isso dá a você um teste OK, embora possa haver um bug.
Stéphane GRILLON

@sgrillon, desculpe por nunca ter respondido isso. Acontece que costumava haver um bug com isso, dependendo da ordem de execução do teste, ele gerava "acertos falsos", onde stubs que foram usados ​​em um teste, mas sobrescritos em outro, o acionariam. No entanto, está consertado há muito tempo, pelo que posso dizer.
Carighan

27

Para mim, nem o @Rulenem as @RunWith(MockitoJUnitRunner.Silent.class)sugestões funcionaram. Era um projeto legado em que atualizamos para o mockito-core 2.23.0.

Poderíamos nos livrar do UnnecessaryStubbingExceptionusando:

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

ao invés de:

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

Desnecessário dizer que você deve preferir olhar para o código de teste, mas primeiro precisamos ter o material compilado e os testes rodando;)


6
NA MINHA HUMILDE OPINIÃO. Esta é a resposta mais útil aqui que encontrei, em vez de silenciar toda a classe de teste.
priyeshdkr

Já que eu queria suprimir apenas 1 zombaria, esta é a melhor resposta para mim. Não é realmente uma resposta para o OP.
Hans Wouters

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

O whenaqui configura sua simulação para fazer algo. No entanto, você não usa mais esse mock de forma alguma após essa linha (além de fazer um verify). Mockito avisa que a whenlinha, portanto, não tem sentido. Talvez você tenha cometido um erro lógico?


Obrigado pela sua ajuda
VHS

Preciso das declarações de quando e de verificação, gentilmente, sugiro como ir adiante
VHS de

2
Chame uma função em sua classe de teste ( Service) para ver se ela reage corretamente. Você não fez nada disso, então o que está testando aqui?
john16384

3

Olhando para uma parte do rastreamento de pilha, parece que você está copiando em dao.doSearch()outro lugar. Mais como criar repetidamente os stubs do mesmo método.

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.

Considere a classe de teste abaixo, por exemplo:

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

Eu preferiria considerar a refatoração de seus testes para stub quando necessário.


2

Se você estiver usando este estilo:

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

substitua-o por:

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

2

Substituir

@RunWith(MockitoJUnitRunner.class)

com

@RunWith(MockitoJUnitRunner.Silent.class)

ou remover@RunWith(MockitoJUnitRunner.class)

ou apenas comente as chamadas mocking indesejadas (mostrado como stub não autorizado).


1

Eu tinha UnnecessaryStubbingExceptionquando tentei usar os whenmétodos em um objeto Spy. Mockito.lenient()silenciou a exceção, mas os resultados do teste não estavam corretos.

No caso de objetos Spy, é necessário chamar os métodos diretamente.

@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

Bem, no meu caso, o erro do Mockito estava me dizendo para chamar o método real após o stub whenou whenever. Como não estávamos invocando as condições que acabamos de zombar, Mockito estava relatando isso como stubs ou código desnecessários.

Aqui está como era quando o erro estava chegando:

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

então, acabei de chamar o método real mencionado na instrução when para simular o método.

as mudanças feitas são como abaixo 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)
}

está funcionando agora.


0

No caso de um grande projeto, é difícil corrigir cada uma dessas exceções. Ao mesmo tempo, o uso Silentnão é recomendado. Eu escrevi um script para remover todos os stubbings desnecessários dada uma lista deles.

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

Precisamos apenas copiar e colar a mvnsaída e escrever a lista dessas exceções usando regex e deixar o script cuidar do resto.


0

Se você usar any () durante a simulação, será necessário espaçar novamente @RunWith (MockitoJUnitRunner.class) com @RunWith (MockitoJUnitRunner.Silent.class).


0

Quando você cria um mock e esse mock não é usado, ele lança uma exceção de stub não usada. No seu caso, esse mock não é realmente chamado. Portanto, ele está jogando esse erro. Portanto, relpace @RunWith(MockitoJUnitRunner.class)com o @RunWith(MockitoJUnitRunner.Silent.class)qual removeria o erro. Se você ainda quiser usar, @RunWith(MockitoJUnitRunner.class)tente depurar sua lógica se a função que você simulou está realmente sendo chamada ou não.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.