Prévia do material em texto
Introdução ao Mockito com Kotlin
O que é?
Mockito é um framework que facilita a criação de mocks - objetos falsos que substituem partes
reais do código.
Para aprender mais sobre mocks e seu papel nos testes confira o artigo Mocks: Introdução a
Automatização de Testes com Mock Object.
Por que é útil?
Mockito melhora o código de teste, porque torna ele mais legível. Além disso, Mockito também
produz mensagens que facilitam rastrear erros.
Características
• Primeiramente escrito para Java, mas foi portado para Kotlin;
• Torna o código mais legível;
• Gera mensagens que facilitam rastrear erros.
Como utilizar: configuração
Configure Mockito em um projeto usando o Gradle, adicionando na sessão dependencies do arquivo
build.gradle.kts as seguintes dependências do Código 1.
testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.3.1")
Código 1. Instalando o Mockito no Gradle
Note que você ainda precisará do JUnit para usar o Mockito.
Após isso, caso seu projeto ainda não tenha uma sessão de testes configurada, insira uma task, ainda
no arquivo build.gradle.kts, conforme o Código 2.
tasks.withType<Test> {
useJUnitPlatform()
}
Código 2. Configuração de testes
A partir disso podemos começar a escrever os primeiros mocks com o Mockito.
Exemplo prático
Para ilustrar como criar um teste usando o Mockito criaremos um pequeno projeto com duas
classes, conforme mostra a Figura 1. A ideia por trás desse microprojeto é simular um pagamento.
Payment representa o pagamento e PaymentGateway a comunicação entre a aplicação e a empresa
que debita o saldo do cartão de crédito do cliente.
Código das classes
A função das classes Payment e PaymentGateway importam mais que seus códigos no contexto
deste artigo.
Entendendo o que elas fazem podemos ter uma ideia de como elas fazem.
Sendo assim, os Códigos 3 e 4 demonstram a resposta de seus métodos em pseudocódigo.
open class PaymentGateway {
open fun request() = "Request payment gateway"
}
Código 3. Classe PaymentGateway
class Payment(private val paymentGateway: PaymentGateway) {
fun pay() = {
// Calcula o valor final
// Aplica descontos
paymentGateway.request()
}
}
Código 4. Classe Payment
Relação entre Payment e PaymentGateway
O objetivo desse exemplo é apresentar uma classe que contém regras de negócio, Payment, que
depende de outra que não possui, PaymentGateway.
Uma vez que PaymentGateway pode falhar, por uma queda na internet, por exemplo, precisamos
isolá-la ao testar Payment. Para isso criamos um mock para PaymentGateway. Assim, caso Payment
falhe isso não ocorrerá ao acaso.
Criando e validando o mock
Antes de criar um mock precisamos criar uma classe de testes , como mostra o Código 5. No
método de teste `Succeeds to pay given a valid payment gateway` inserimos o código de teste.
internal class PaymentTest {
@Test
fun `Succeeds to pay given a valid payment gateway`() {
val mock = mock<PaymentGateway> {
on { request() } doReturn "mock implementation"
}
val payment = Payment(mock)
payment.pay()
verify(mock).request()
}
}
Código 5. Classe de teste
Na Linha 5 criamos o mock usando o método mock do Mockito.
Na Linha 6, estamos substituindo o código real do método PaymentGateway::request() por um
código falso. Ele vai fazer com que a string "mock implementation" seja retornada sempre que esse
método for chamado.
A instrução on { request() } doReturn "mock implementation", na Linha 6, pode ser lida
como "para o método request() quando ele for invocado, retorne 'mock implementation'".
A técnica de criar código falso para os mocks é chamada stubbing.
Na Linha 9 passamos o mock para o construtor de Payment.
Na Linha 10, invocamos o método Payment::pay() do Código 4. Internamente, o método
Payment::pay() deve invocar PaymentGateway::request(), fazendo com nosso stub seja executado.
Na Linha 12 fazemos o assertion, a verificação que determina se o teste falhou ou não. Nessa
assertion usamos Mockito::verify() para checar se o método PaymentGateway::request() foi
invocado, como mostra a Figura 2.
Figura 2. Teste bem-sucedido
Nesse caso, uma vez que PaymentGateway::request() foi invocado, o teste foi bem-sucedido.
Produzindo e analisando uma falha
Caso se deseje ver o teste falhar, podemos comentar a invocação de PaymentGateway::request() em
Payment::pay(), conforme o Código 6.
class Payment(private val paymentGateway: PaymentGateway) {
fun pay() {
// paymentGateway.request()
}
}
Código 6. PaymentGateway::request() em Payment comentado
Ao fazermos isso, Mockito::verify() saberá que PaymentGateway::request() em Payment não foi
invocado, pois está comentado, e falhará o teste com uma mensagem como a apresentada
no Código 7 e na Figura 3.
Wanted but not invoked:
paymentGateway.request();
-> at PaymentTest.Test mock(PaymentTest.kt:19)
Actually, there were zero interactions with this mock.
Código 7. Mensagem de erro gerada pelo Mockito
A mensagem nos permite rastrear o erro, uma vez que mostra onde o problema
ocorreu, PaymentTest.kt:19", e o que o ocasionou, "Wanted but not invoked:
paymentGateway.request();".
Final classes
Anteriormente, usamos o modificador open na classe PaymentGateway apresentado no Código 8.
open class PaymentGateway {
…
}
Código 8. Classe PaymentGateway com o modificar open
Mockito não funciona com classes final, padrão do Kotlin, sem alguma configuração.
Se tentarmos executar novamente o teste após remover open da classe PaymentGateway
receberemos o erro apresentado no Código 9.
Cannot mock/spy class PaymentGateway
Mockito cannot mock/spy because :
- final class
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class PaymentGateway
Mockito cannot mock/spy because :
- final class
Código 9. Erro exibido ao remover open de PaymentGateway
Além de usar open para contornar esse problema, podemos configurar o Mockito de forma que ele
saiba o que fazer nessa condição.
Para isso crie uma arquivo chamado org.mockito.plugins.MockMaker na pasta
src/test/resources/mockito-extensions com conteúdo do Código 10.
mock-maker-inline
Código 10. Configuração no arquivo org.mockito.plugins.MockMaker
A partir disso podemos remover o modificador open da classe PaymentGateway e nosso código
continuará funcionando conforme esperado.
Conclusão
Atualmente, escrevemos mais códigos de testes do que qualquer outro em aplicações. Por este
motivo, dominar técnicas triviais de teste como mocking e stubbing é fundamental para o
programador. Nesse artigo você aprendeu como usar o Mockito para essa tarefa, escrevendo código
de testes mais limpo e que produzem mensagens mais eficientes quanto a rastreabilidade de erros.
Introdução ao Mockito com Kotlin
O que é?
Por que é útil?
Características
Como utilizar: configuração
Exemplo prático
Código das classes
Relação entre Payment e PaymentGateway
Criando e validando o mock
Produzindo e analisando uma falha
Final classes
Conclusão