Guia rápido do BDDMockito
1. Visão geral
O termo BDD foi cunhado primeiro porDan North – back in 2006.
O BDD incentiva a escrita de testes em uma linguagem natural, legível por humanos, que se concentra no comportamento do aplicativo.
Ele define uma maneira claramente estruturada de escrever testes após três seções (Organizar, Agir, Afirmar):
-
given algumas pré-condições (organizar)
-
when uma ação ocorre (Act)
-
then verifica a saída (Assert)
The Mockito library is shipped with a BDDMockito class which introduces BDD-friendly APIs. Esta API nos permite adotar uma abordagem mais amigável ao BDD, organizando nossos testes usandogiven()e fazendo asserções usandothen().
Neste artigo, vamos explicar como configurar nossos testes Mockito baseados em BDD. Também falaremos sobre as diferenças entre as APIsMockito eBDDMockito, para eventualmente focar na APIBDDMockito.
2. Configuração
2.1. Dependências do Maven
The BDD flavor of Mockito is part of the mockito-core library, para começar, precisamos apenas incluir o artefato:
org.mockito
mockito-core
2.21.0
Para obter a versão mais recente do Mockito, verifiqueMaven Central.
2.2. Importações
Nossos testes podem se tornar mais legíveis se incluirmos a seguinte importação estática:
import static org.mockito.BDDMockito.*;
Observe queBDDMockito estendeMockito, então não perderemos nenhum recurso fornecido pela APIMockito tradicional.
3. Mockito vs. BDDMockito
A simulação tradicional no Mockito é realizada usandowhen(obj).then*() na etapa de arranjo.
Posteriormente, a interação com nosso mock pode ser validada usandoverify() na etapa Assert.
BDDMockito fornece aliases BDD para vários métodosMockito, portanto, podemos escrever nossa etapa de arranjo usandogiven (em vez dewhen), da mesma forma, poderíamos escrever nossa etapa Assert usandothen (em vez deverify).
Vejamos um exemplo de corpo de teste usando o Mockito tradicional:
when(phoneBookRepository.contains(momContactName))
.thenReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
verify(phoneBookRepository)
.insert(momContactName, momPhoneNumber);
Vamos ver como isso se compara aBDDMockito:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
then(phoneBookRepository)
.should()
.insert(momContactName, momPhoneNumber);
4. Zombando comBDDMockito
Vamos tentar testar oPhoneBookService onde precisaremos simular oPhoneBookRepository:
public class PhoneBookService {
private PhoneBookRepository phoneBookRepository;
public void register(String name, String phone) {
if(!name.isEmpty() && !phone.isEmpty()
&& !phoneBookRepository.contains(name)) {
phoneBookRepository.insert(name, phone);
}
}
public String search(String name) {
if(!name.isEmpty() && phoneBookRepository.contains(name)) {
return phoneBookRepository.getPhoneNumberByContactName(name);
}
return null;
}
}
BDDMockito comoMockito nos permite retornar um valor que pode ser fixo ou dinâmico. Também nos permitiria lançar uma exceção:
4.1. Retornando um valor fixo
UsandoBDDMockito,, poderíamos facilmente configurar o Mockito para retornar um resultado fixo sempre que nosso método de destino de objeto fictício for invocado:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(xContactName, "");
then(phoneBookRepository)
.should(never())
.insert(momContactName, momPhoneNumber);
4.2. Retornando um valor dinâmico
BDDMockito nos permite fornecer uma maneira mais sofisticada de retornar valores. Poderíamos retornar um resultado dinâmico com base na entrada:
given(phoneBookRepository.contains(momContactName))
.willReturn(true);
given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
.will((InvocationOnMock invocation) ->
invocation.getArgument(0).equals(momContactName)
? momPhoneNumber
: null);
phoneBookService.search(momContactName);
then(phoneBookRepository)
.should()
.getPhoneNumberByContactName(momContactName);
4.3. Lançando uma exceção
Dizer a Mockito para lançar uma exceção é bem direto:
given(phoneBookRepository.contains(xContactName))
.willReturn(false);
willThrow(new RuntimeException())
.given(phoneBookRepository)
.insert(any(String.class), eq(tooLongPhoneNumber));
try {
phoneBookService.register(xContactName, tooLongPhoneNumber);
fail("Should throw exception");
} catch (RuntimeException ex) { }
then(phoneBookRepository)
.should(never())
.insert(momContactName, tooLongPhoneNumber);
Observe como trocamos as posições degivenewill*, isso é obrigatório no caso de estarmos zombando de um método que não tem valor de retorno.
Observe também que usamos correspondências de argumento como (any,eq) para fornecer uma forma mais genérica de simulação baseada em critérios em vez de depender de um valor fixo.
5. Conclusão
Neste tutorial rápido, discutimos como o BDDMockito tenta trazer uma semelhança com o BDD aos nossos testes Mockito e discutimos algumas das diferenças entreMockitoeBDDMockito.
Como sempre, o código-fonte pode ser encontradoover on GitHub - dentro do pacote de testecom.example.bddmockito.