Sempre que estamos falando sobre boas práticas de desenvolvimento de software, um nome sempre aparece: testes de unidade. De acordo com Marco Tulio Valente (2020), testes de unidade são testes automatizados de pequenas unidades de código, normalmente classes, as quais são testadas de forma isolada do restante do sistema. Um teste de unidade é um programa que chama métodos de uma classe e verifica se eles retornam os resultados esperados. Esses testes podem ser feitos na maioria das linguagens de programação e cada uma delas possui bibliotecas e recursos para facilitar o desenvolvimento dos mesmos. A ideia do post é mostrar como fazer testes de unidade em kotlin usando mockK e AssertJ.
MockK
Mockk é uma biblioteca de mocking para kotlin que traz uma série de recursos como Spy, Relaxed mock, Partial mocking, Object mocks, Class mock entre outros.
AssertJ
O AssertJ fornece um rico conjunto de asserções, mensagens de erro realmente úteis, melhora a legibilidade do código de teste e foi projetado para ser super fácil de usar em sua IDE favorita.
Dependências
Para criar testes de unidade com as bibliotecas informadas, é necessário adicionar as dependências no projeto. No caso do maven basta colocar o trecho a seguir no pom.xml
(verificar versões)
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<version>1.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
Mãos na massa
Suponha que é necessário testar o comportamento de um método que lista produtos. O método possui dois fluxos. Quando é informado o nome do produto a ser listado, deve-se retornar apenas o produto especificado. Quando o nome do produto não é informado, deve-se retornar todos os produtos.
// Resto do código omitido
fun listar(nomeProduto: String?): List<Produto> {
return nomeProduto?.let {
repository.findByProdutoNome(nomeProduto)
} ?: repository.findAll()
}
O primeiro teste será para validar o comportamento do método quando é informado o nome do produto.
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.*
// Outros imports foram omitidos
class ProdutoServiceTest {
private val repository: ProdutoRepository = mockk()
private val produtoService = ProdutoService(repository)
@Test
fun `deve listar produto por nome`() {
every { repository.findByProdutoNome(any()) } returns listOf(Produto("Geladeira"))
val result = produtoService.listar(any())
verify(exactly = 0) { produtoRepository.findAll() }
verify(exactly = 1) { produtoRepository.findByProdutoNome(any()) }
assertThat(resultado).isNotNull
assertThat(resultado).hasSize(1)
}
}
Vamos entender alguns recursos apresentados. Como a ideia do teste de unidade é testar apenas o comportamento do método que contém a regra de negócio, toda a parte de repositório deverá ser mockada. Para mockar objetos com o mockK pode-se utilizar a inline function mockk()
. Há duas formas de utilizá-la, da forma que foi declarada acima ou com mockk<ProdutoRepository>()
. Com o repositório mockado, é necessário definir o seu comportamento. Para isto é criado o bloco every
que descreve qual resposta deve ser dada quando um método do repositório for chamado. No caso acima, sempre que o método findByProdutoNome
for chamado, será retornado uma lista de produtos que possui apenas um produto. O verify
será o bloco responsável por verificar se o mock foi invocado como esperado. Como o método possui dois fluxos, temos que garantir que quando for informado o nome do produto, o método do repositório findAll()
não seja chamado e sim o findByProdutoNome()
. Qualquer comportamento diferente deste deverá fazer o teste falhar. Tudo que foi falado até aqui são recursos do mockk e já seria suficiente para fazer o teste ser executado com sucesso. Porém para garantir que o resultado retornado está correto, podemos fazer asserções e é neste momento que o AssertJ vira protagonista. Para escrever uma asserção, você sempre precisa começar passando seu objeto para o método assertThat()
e então seguir com as asserções reais. No caso acima, estamos afirmando que o resultado não é nulo e a lista possui um elemento. Qualquer retorno diferente disso, significaria que o método listar
de ProdutoService
não está de acordo com o comportamento esperado, e mais uma vez o teste deverá falhar. Agora que entendemos todos os recursos utilizados no teste, podemos testar o outro fluxo, quando não é informado o nome do produto.
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.*
// Outros imports foram omitidos
class ProdutoServiceTest {
private val repository: ProdutoRepository = mockk()
private val produtoService = ProdutoService(repository)
@Test
fun `deve listar todos os produtos`() {
every { repository.findAll() } returns listOf(Produto("Geladeira"), Produto("Televisao"))
val result = produtoService.listar()
verify(exactly = 1) { produtoRepository.findAll() }
verify(exactly = 0) { produtoRepository.findByProdutoNome(any()) }
assertThat(resultado).isNotNull
assertThat(resultado).hasSize(2)
}
Conclusão
Podemos ver que ambas as bibliotecas nos fornecem recursos poderosos para criar testes de unidades simples, elegantes e eficientes. Existem muitos outros recursos a serem explorados, o que não caberia em apenas um post. Para quem tiver interesse em continuar os estudos, abaixo, em referências, deixei alguns links que vão auxiliar. Espero que tenham gostado da leitura e qualquer dúvida, crítica ou sugestão, não deixem de comentar. Até a próxima!!
Referências
https://mockk.io/
https://joel-costigliola.github.io/assertj/
https://www.baeldung.com/kotlin/mockk
https://www.baeldung.com/introduction-to-assertj
Bibliografia
Marco Tulio Valente. Engenharia de Software Moderna: Princípios e Práticas para Desenvolvimento de Software com Produtividade, 2020.
Top comments (5)
Que conteúdo sensacional. Parabéns, ajudou muito!
Que bom que gostou, Rodrigo. Fico feliz que te ajudou =)
Thanks
Wow!!!
Sempre bom ler mais sobre o Mockk! Parabéns