Mockito Strict Stubbing e The UnnecessaryStubbingException
1. Visão geral
In this quick tutorial, we’ll learn about the Mockito UnnecessaryStubbingException. Esta exceção é uma das exceções comuns que provavelmente encontraremos ao usar stubs incorretamente.
Começaremos explicando a filosofia por trás do stubbing estrito e por que o Mockito incentiva seu uso por padrão. A seguir, veremos exatamente o que essa exceção significa e em que circunstâncias ela pode ocorrer. Para concluir, veremos um exemplo de como podemos suprimir essa exceção em nossos testes.
Para saber mais sobre como testar o Mockito, verifique nosso abrangenteMockito series.
2. Stubbing estrito
Com a versão 1.x do Mockito, era possível configurar e interagir com zombarias sem nenhum tipo de restrição. Isso significava que, com o tempo, os testes frequentemente se tornavam complicados demais e, às vezes, mais difíceis de depurar.
Desde a versão 2. +,Mockito has been introducing new features that nudge the framework towards “strictness”. Os principais objetivos por trás disso são:
-
Detectar stubs não utilizados no código de teste
-
Reduz a duplicação do código de teste e unnecessary test code
-
Promova testes mais limpos removendo o código "morto"
-
Ajude a melhorar a debuggability e a produtividade
Following these principles helps us create cleaner tests by eliminating unnecessary test code. Eles também ajudam a evitar erros de copiar e colar, além de outros problemas de desenvolvedor.
Para resumir, stub estrito relata stubs desnecessários, detecta incompatibilidade de argumento de stub e torna nossos testes mais DRY (Don't Repeat Yourself). Isso facilita um clean and maintainable codebase.
2.1. Configurando Stubs estritos
Desde o Mockito 2. +, o stubbing estrito é usado por padrão ao inicializar nossos mocks usando um dos seguintes:
-
MockitoJUnitRunner
-
MockitoJUnit.rule()
Mockito strongly recommends the use of either of the above. No entanto, também há outra maneira de habilitar o stub estrito em nossos testes quando não estamos aproveitando a regra ou o executor Mockito:
Mockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.STRICT_STUBS)
.startMocking();
Um último ponto importante a fazer é que no Mockito 3.0, todos os stubbings serão “restritos” e validados por padrão.
3. UnnecessaryStubbingException Exemplo
Simplificando, um stub desnecessário é uma chamada de método em stub que nunca foi realizada durante a execução do teste.
Vamos dar uma olhada em um exemplo simples:
@Test
public void givenUnusedStub_whenInvokingGetThenThrowUnnecessaryStubbingException() {
when(mockList.add("one")).thenReturn(true); // this won't get called
when(mockList.get(anyInt())).thenReturn("hello");
assertEquals("List should contain hello", "hello", mockList.get(1));
}
Quando executamos este teste de unidade, o Mockito detectará o stub não utilizado e lançará umUnnecessaryStubbingException:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at com.example.mockito.misusing.MockitoUnecessaryStubUnitTest.givenUnusedStub_whenInvokingGetThenThrowUnnecessaryStubbingException(MockitoUnecessaryStubUnitTest.java:37)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
Felizmente, é bastante claro na mensagem de erro qual é o problema aqui. Também podemos ver que a mensagem de exceção nos aponta a linha exata que causa o erro.
Por que isso acontece? Bem, a primeira invocação dewhen configura nosso mock para retornartrue quando chamamos o métodoadd com o argumento“one”. No entanto, não invocamos esse método durante o restante da execução do teste de unidade.
Mockito está nos dizendo que nossa primeira linhawhen é redundante e talvez tenhamos cometido um erro ao configurar nossos stubs.
Embora este exemplo seja trivial, é fácil imaginar, ao zombar de uma hierarquia complexa de objetos, como esse tipo de mensagem pode ajudar na depuração e ser muito útil.
4. Ignorando Stubbing estrito
Finally, let’s see how to bypass strict stubs. Isso também é conhecido como stub suave.
Às vezes, precisamos configurar a stubbing específica para ser branda, mantendo todas as outras stubbings e zombarias para usar a stub estrita:
@Test
public void givenLenientdStub_whenInvokingGetThenThrowUnnecessaryStubbingException() {
lenient().when(mockList.add("one")).thenReturn(true);
when(mockList.get(anyInt())).thenReturn("hello");
assertEquals("List should contain hello", "hello", mockList.get(1));
}
No exemplo acima,we use the static method Mockito.lenient() to enable the lenient stubbing on the add method of our mock list.
Os stubs lenientes ignoram as regras de validação de "stub estrito". Por exemplo, quando o stub é declarado como leniente, ele não será verificado quanto a possíveis problemas de stub, como o stub desnecessário descrito anteriormente.
5. Conclusão
Neste breve artigo, começamos apresentando o conceito de stubbing estrito no Mockito e entendemos a filosofia por trás de por que ele foi introduzido e por que é importante.
A seguir, vimos um exemplo deUnnecessaryStubbingException antes de terminar com um exemplo de como habilitar o stub flexível em nossos testes.
Como sempre, o código-fonte completo do artigo está disponível emGitHub.