Mocks e Fixtures em Kotlin: Guia de Testes Sem Dor

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que testes sem mocks são como café sem cafeína?

\n\n

Olá, colega! Se você programa em Kotlin e quer que seu código não quebre a cada segundo commit, mais cedo ou mais tarde você vai chegar aos testes. E nos testes, aos mock objects e fixtures. Sem eles, os testes de unidade viram testes de integração: você não testa uma classe, mas uma cadeia inteira de dependências. Se o banco de dados cair ou a API retornar 500, o teste falha, mesmo que seu código esteja impecável.

\n\n

Neste artigo, vamos ver como usar corretamente Mocks e Fixtures em testes Kotlin para que seus testes sejam rápidos, confiáveis e digam a verdade, e não "tudo quebrou, conserte". Vamos nessa!

\n\n

O que é um mock e por que você precisa dele em Kotlin?

\n\n

Um mock object é uma simulação de um objeto real. Em vez de levantar um servidor ou banco de dados inteiro, criamos um "simulacro" que se comporta exatamente como determinamos. Isso permite isolar o código testado do mundo externo.

\n\n

Conceitos principais

\n
    \n
  • Mock — um objeto cujo comportamento dos métodos controlamos. Podemos definir o que ele retorna e verificar se foi chamado.
  • \n
  • Stub — um caso especial de mock que apenas retorna valores predefinidos. Não verifica chamadas.
  • \n
  • Fixture — um conjunto de dados ou estados pré-preparados necessários para executar um teste. Por exemplo, um usuário de teste ou configurações de banco.
  • \n
\n\n

No mundo Kotlin, existem dois grandes players: MockK (nativo do Kotlin) e Mockito (popular no Java). Vamos usar o MockK — ele se dá melhor com corrotinas, funções de extensão e sealed classes.

\n\n

Configuração do projeto: adicionando MockK e JUnit 5

\n\n

Primeiro, adicione as dependências no build.gradle.kts:

\n\n
dependencies {\n    testImplementation("io.mockk:mockk:1.13.8")\n    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n}\n
\n\n

Agora podemos escrever testes. Vamos criar um serviço simples e testá-lo com mocks.

\n\n

Primeiro exemplo: simulando um repositório

\n\n

Imagine que temos um UserService que depende de UserRepository:

\n\n
class UserService(private val repository: UserRepository) {\n    fun getUserName(id: Long): String {\n        val user = repository.findById(id)\n            ?: throw IllegalArgumentException("User not found")\n        return user.name\n    }\n}\n
\n\n

Agora vamos escrever um teste usando um mock object para o repositório:

\n\n
import io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\n\nclass UserServiceTest {\n\n    private val repository = mockk<UserRepository>()\n    private val service = UserService(repository)\n\n    @Test\n    fun `deve retornar nome do usuário quando usuário existe`() {\n        // Arrange\n        every { repository.findById(1L) } returns User(1L, "Alice")\n\n        // Act\n        val name = service.getUserName(1L)\n\n        // Assert\n        assertEquals("Alice", name)\n        verify { repository.findById(1L) }\n    }\n\n    @Test\n    fun `deve lançar exceção quando usuário não for encontrado`() {\n        // Arrange\n        every { repository.findById(any()) } returns null\n\n        // Act & Assert\n        assertFailsWith<IllegalArgumentException> {\n            service.getUserName(99L)\n        }\n    }\n}\n
\n\n

Observe: every define o comportamento do mock, verify verifica se o método foi chamado. Essa é a base dos mocks e fixtures em testes Kotlin.

\n\n

Fixtures: preparando dados com antecedência

\n\n

Quando há muitos testes, duplicar a criação de dados é uma má prática. É aí que entram as fixtures. Em Kotlin, é conveniente

Blogs

Book Recommendations