MockK é um framework Kotlin-first para criar objetos mock, projetado especificamente para o ecossistema Kotlin, considerando suas características linguísticas: corrotinas, funções de extensão, data class e sealed class. Ao contrário do Mockito, que foi portado para Kotlin a partir do Java, o MockK foi originalmente projetado para a sintaxe Kotlin e não requer plugins adicionais para trabalhar com classes final. De acordo com MockK.io, a biblioteca é usada em mais de 40% dos projetos Kotlin com testes unitários.
Principais pontos
MockK é uma biblioteca para criar objetos mock, escrita em Kotlin e otimizada para sua sintaxe. Ela resolve os mesmos problemas que o Mockito — isolar o código testado das dependências — mas o faz usando construções específicas do Kotlin: lambdas, DSL, reified generics e funções suspend.
A principal vantagem do MockK sobre soluções portadas é o suporte nativo ao Kotlin. No Mockito, simular uma classe final requer opt-in (mockito-inline), e métodos estáticos requerem mockStatic. O MockK suporta isso por padrão, já que as classes Kotlin são final por padrão, e contornar essa limitação está incorporado na arquitetura da biblioteca.
A versão 1.13.12 (2024) é uma versão estável que suporta Kotlin 2.0, o compilador K2 e projetos multiplataforma (KMP). O MockK também funciona com Kotlin/Native e Kotlin/JS, tornando-se a única escolha para projetos KMP onde nem Mockito nem EasyMock são aplicáveis.
O MockK é projetado considerando as especificidades do Kotlin e utiliza recursos da linguagem — reified generics, DSL com lambdas, funções inline — para fornecer uma API concisa e type-safe sem sacrificar o desempenho.
O mecanismo do MockK é baseado em instrumentação de bytecode através da biblioteca ByteBuddy (como o Mockito), mas a envolve em um DSL amigável ao Kotlin. Em vez de cadeias when().thenReturn(), o MockK usa blocos lambda every { } e coEvery { } que parecem uma extensão natural da linguagem. Internamente, o MockK intercepta a chamada dentro do lambda, analisa o método e os argumentos através de reflexão e os compara com as regras de stubbing registradas.
O bloco every { mock.method() } returns value é lido como “toda vez que o método é chamado, retorne o valor.” Essa sintaxe declarativa está mais próxima do estilo Kotlin e elimina a confusão com a ordem dos argumentos em when(). Graças aos reified generics do Kotlin, o tipo do mock é inferido automaticamente sem especificar explicitamente a classe.
val repository = mockk<UserRepository>()
// Stubbing: cada chamada a findById(1) retorna o usuário
every { repository.findById(1) } returns User("Alice")
// Chamada e verificação
val result = repository.findById(1)
assertEquals("Alice", result.name)
Ao contrário do Mockito, onde cada método deve ser configurado explicitamente, o MockK suporta relaxed mock — um mock que retorna valores padrão “ razoáveis” para qualquer método: uma lista vazia para List, 0 para Int, uma string vazia para String. Isso reduz drasticamente a quantidade de código de configuração.
// Relaxed mock — todos os métodos retornam valores padrão
val api = mockk<ApiService>(relaxed = true)
// Não requer stubbing — retorna uma lista vazia
println(api.getUsers()) // []
MockK oferece várias maneiras de criar objetos mock: mockk<T>() para mocks estritos (cada método deve ser configurado explicitamente), mockk<T>(relaxed = true) para mocks relaxados e spyk(obj) para criar um spy sobre um objeto real.
| Função | Tipo | Comportamento sem stubbing |
|---|---|---|
| mockk() | Mock estrito | Lança exceção ao chamar método não configurado |
| mockk(relaxed = true) | Mock relaxado | Retorna valor padrão |
| spyk() | Spy | Chama o método real se nenhum stub estiver configurado |
| slot() | Argument Captor | Captura o argumento para verificação |
A escolha entre mock estrito e relaxado depende do contexto. Um mock estrito garante que o teste não use métodos cujo comportamento não esteja definido — isso aumenta a confiabilidade. Um mock relaxado é conveniente para prototipagem rápida de testes onde nem todas as dependências são importantes. Na prática, recomenda-se começar com um mock estrito e mudar para relaxado apenas quando o stubbing ocupar mais linhas que o próprio teste.
O bloco every é a construção central de stubbing no MockK. Dentro do lambda, uma chamada de método com argumentos específicos é descrita, e então um valor é retornado via returns, uma exceção é lançada via throws, ou uma resposta é calculada via answers.
MockK suporta todos os cenários necessários para testes: retornar um valor, lançar uma exceção, calcular uma resposta com base em argumentos e múltiplas respostas em ordem (sequência de chamadas).
// Retornar valor
every { repo.findById(1) } returns User("Alice")
// Lançar exceção
every { repo.findById(999) } throws NotFoundException()
// Resposta dinâmica
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// Sequência de respostas
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify no MockK é semelhante ao Mockito.verify() em propósito, mas usa DSL Kotlin: verify { mock.method() }. Para funções suspend, usa-se coVerify { mock.suspendMethod() }, que funciona corretamente com corrotinas e não requer um runner especial.
MockK suporta os mesmos modificadores que o Mockito: exactly(1), atLeast(2), atMost(5), wasNot(Called). A sintaxe é minimalista — o modificador é passado como primeiro argumento em verify { }.
// Verificação: método chamado exatamente 1 vez
verify(exactly = 1) { repo.save(any()) }
// Verificar ordem de chamadas
verifySequence {
repo.save(any())
repo.flush()
}
// coVerify para funções suspend
coVerify { api.fetchUsers() }
Para verificação de argumentos, usa-se slot() — um análogo do ArgumentCaptor. Um slot é declarado antes da chamada, passado para every ou verify, e após a execução do teste contém o valor capturado.
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK fornece as anotações @MockK e @RelaxedMockK para criar mocks através de inicialização no JUnit 5. A extensão MockKExtension cria automaticamente mocks antes de cada teste e limpa depois — semelhante ao MockitoExtension, mas com suporte ao modo relaxado.
A anotação @InjectMockKs (ou a alternativa @MockK com criação explícita de objeto) injeta mocks na instância testada. Isso reduz o código repetitivo e torna o código do teste mais limpo.
@ExtendWith(MockKExtension::class)
class UserServiceTest {
@MockK
lateinit var repository: UserRepository
@InjectMockKs
lateinit var service: UserService
@Test
fun `getUser returns user from repository`() {
every { repository.findById(1) } returns User("Alice")
assertEquals("Alice", service.getUser(1)?.name)
}
}
A escolha entre MockK e Mockito depende da composição da equipe e do tipo de projeto. O Mockito tem um ecossistema maior, mais exemplos e mais integrações, mas o MockK oferece uma sintaxe Kotlin mais limpa e suporte nativo para recursos da linguagem. Para novos projetos Kotlin, o MockK é recomendado como a solução mais idiomática.
| Critério | MockK | Mockito |
|---|---|---|
| Sintaxe | DSL Kotlin (every, verify) | Estilo Java (when, thenReturn) |
| Corrotinas | coEvery, coVerify (nativo) | Requer bibliotecas adicionais |
| Classe final | Suportado por padrão | Requer mockito-inline |
| KMP | Suportado | Não suportado |
| Relaxed mock | Integrado | Sem equivalente |
| Popularidade | Crescente na comunidade Kotlin | Domina em projetos Java e híbridos |
Para projetos em Kotlin puro (sem classes Java), o MockK é preferível: menos código repetitivo, suporte nativo a corrotinas, sem surpresas com classes final. Para projetos híbridos ou equipes com experiência em Java, o Mockito continua sendo uma opção viável — ambas as bibliotecas podem ser usadas no mesmo projeto através de diferentes módulos. Ao migrar do Mockito para o MockK, basta substituir as anotações @Mock por @MockK e reescrever os blocos when().thenReturn() para o formato every { }.
Perguntas frequentes
Relaxed mock retorna valores padrão para todos os métodos não configurados (lista vazia, 0, null) sem lançar exceções. Um mock comum (estrito) requer stubbing explícito de cada método — caso contrário, o teste falha. O relaxed mock é conveniente para testes rápidos, o estrito para testes confiáveis.
MockK suporta a simulação de funções de extensão através de mockkStatic(). Isso é possível porque funções de extensão em Kotlin são métodos estáticos com o receptor como primeiro parâmetro. Para cada função de extensão, você precisa especificar a classe na qual ela é declarada.
Sim, MockK suporta Kotlin Multiplatform (KMP) para código comum. Nas plataformas JVM, Native e JS, você pode usar a API comum: mockk(), every, verify. Isso torna o MockK a única escolha para projetos KMP onde o Mockito não funciona.
Use verifySequence { } — um bloco onde as chamadas são especificadas estritamente na ordem esperada. Se a ordem real diferir, o verifySequence lançará uma exceção indicando a primeira chamada incompatível.
Sim, tecnicamente é possível, mas não recomendado. Conflitos podem ocorrer no nível de instrumentação de bytecode (ByteBuddy vs mockito-inline). Se um projeto já usa Mockito, a migração para MockK pode ser gradual através do isolamento de módulos.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também