Mock é um objeto substituto que imita o comportamento de um componente real e permite verificar interações com ele. Ao contrário de Stub, que simplesmente retorna um valor pré-definido, Mock registra o fato da invocação do método, os argumentos passados e o número de chamadas. De acordo com Mockito (2024), Mock é o tipo mais popular de Test Double em projetos Java e Kotlin, usado em mais de 70% dos testes unitários de aplicativos móveis.
Pontos principais
Mock é um objeto criado por um framework de mocking (Mockito, MockK, EasyMock) que simula uma interface ou classe e registra todas as chamadas aos seus métodos. O desenvolvedor define expectativas: o método X será chamado com os argumentos Y e retornará Z. Após a execução do teste, o Mock verifica se as expectativas corresponderam às chamadas reais.
O termo vem da metáfora teatral de Test Doubles: um Mock é um “impostor” que não apenas fica no palco (como um Dummy) mas desempenha um papel e verifica se a interação com ele foi correta. Se o código sob teste não chamou o método que o Mock esperava, ou o chamou com argumentos incorretos — o teste falha com uma mensagem de expectativa violada.
Um Mock é criado através da fábrica do framework: mockk<MyInterface>() ou Mockito.mock(MyClass.java). O framework gera um objeto proxy que intercepta todas as chamadas de métodos. Cada chamada é comparada com expectativas pré-definidas. Se a chamada corresponder a uma expectativa — o valor especificado é retornado. Se não — o Mock retorna um valor padrão ou lança uma exceção, dependendo da configuração.
Mock é essencial quando o código sob teste interage com componentes que têm efeitos colaterais: enviar dados para um servidor, escrever em um banco de dados, logging, analytics, navegação, exibir diálogos do sistema. Sem Mocks, essas interações não podem ser verificadas sem executar infraestrutura real. De acordo com o Google Testing Blog, Mock é a única maneira de verificar se um aplicativo realmente enviou um evento de analytics sem iniciar um servidor de teste.
A diferença entre Mock e Stub é um dos tópicos mais debatidos em testes. Ambos os tipos substituem uma dependência real, mas de formas fundamentalmente diferentes.
| Critério | Mock | Stub |
|---|---|---|
| Pergunta principal | O método foi chamado? | Qual resultado foi retornado? |
| Verificação | Comportamento (verify) | Estado (assert) |
| Retorno de dados | Opcional | Obrigatório |
| Exemplo | verify(analytics).logEvent(“click”) | assertEquals(5, repository.getCount()) |
| Quando usar | Efeitos colaterais | Retorno de dados |
Um teste simples para decidir: pergunte a si mesmo “se eu excluir esta linha de código, o teste falhará?” Se o teste verifica um valor de retorno — você precisa de um Stub (verificação baseada em assert). Se o teste verifica se o código chamou um método com os argumentos corretos — você precisa de um Mock (verificação baseada em verify). Esta dicotomia segue o padrão Command-Query Separation: métodos que mudam estado (commands) precisam de Mocks; métodos que retornam dados (queries) precisam de Stubs.
Escolher entre Mockito e MockK é uma das primeiras decisões ao configurar o stack de testes para um projeto Android em Kotlin. Ambas as bibliotecas cumprem o mesmo propósito, mas com abordagens diferentes para recursos específicos do Kotlin.
Mockito é o padrão de facto para projetos Java. A versão 5.x suporta mocking para classes finais, métodos estáticos e construtores graças ao MockMaker embutido. Para projetos Kotlin, o Mockito requer configuração adicional: extensões mockito-kotlin para sintaxe melhorada, mockito-inline para classes finais. O Mockito não suporta corrotinas Kotlin e funções suspend sem adaptadores adicionais.
MockK foi criado especificamente para Kotlin. Ele suporta nativamente corrotinas (coEvery, coVerify), classes seladas, classes de dados, singletons de objeto e funções de extensão. A sintaxe do MockK usa DSL com blocos lambda, o que parece natural em código Kotlin. O MockK também suporta mocking de propriedades sem configuração adicional — isso é importante para projetos Android que usam LiveData, StateFlow e Delegates.
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)
// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user
Benchmarks (JVM Benchmark, 2024) mostram que MockK cria objetos mock 15–20% mais rápido que o Mockito para projetos Kotlin graças ao trabalho direto com bytecode Kotlin em vez de Java Reflections. Para projetos com milhares de testes unitários, a diferença na velocidade de build pode ser notável: o MockK economiza 30–60 segundos em uma execução completa de testes em projetos grandes.
Vamos examinar três cenários: testar um ViewModel com dependências Mock, testar um UseCase verificando chamadas de API e testar corrotinas com coVerify.
class ProfileViewModelTest {
private val analytics = mockk<AnalyticsService>()
private val repo = mockk<UserRepository>()
private val vm = ProfileViewModel(repo, analytics)
fun `profile opened logs analytics event`() {
every { analytics.logEvent("profile_opened") } returns Unit
vm.onViewCreated()
verify { analytics.logEvent("profile_opened") }
}
}
class SendMessageUseCaseTest {
private val api = mockk<MessagingApi>()
private val useCase = SendMessageUseCase(api)
fun `send message with correct payload`() = runTest {
val message = Message(text = "Hello", userId = 42)
coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")
val result = useCase.execute(message)
coVerify {
api.sendMessage(match {
it.text == "Hello" && it.userId == 42
})
}
assertTrue(result is MessageResult.Sent)
}
}
class OrderUseCaseTest {
private val api = mockk<OrderApi>()
private val useCase = OrderUseCase(api)
private val slot = slot<OrderRequest>()
fun `order request contains correct items`() = runTest {
coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")
useCase.execute(listOf("item_a", "item_b"))
assertEquals(2, slot.captured.items.size)
assertEquals("item_a", slot.captured.items[0])
}
}
O uso eficaz de Mock no desenvolvimento móvel requer disciplina. Violar essas regras transforma os testes em obstáculos frágeis que quebram a cada refatoração.
Uma regra rígida: Mocks devem ser criados apenas para dependências que cruzam o limite da aplicação: clientes de API, bancos de dados, sistema de arquivos, serviços do sistema (LocationManager, BluetoothAdapter, Camera). Classes internas da aplicação — entidades de domínio, Value Objects, utilitários simples — não devem ser substituídas por Mocks. Seu comportamento é testado através de objetos reais.
Cada teste deve conter exatamente uma verificação lógica — ou verify (para Mock) ou assert (para Stub). Não misture verificação de estado e comportamento em um único teste. Se você precisa verificar tanto uma chamada de API quanto seu resultado — crie dois testes separados com nomes diferentes. Esta regra, conhecida como “um assert por teste”, remonta às recomendações de Kent Beck (2002).
Além do mocking básico, existem técnicas avançadas que resolvem tarefas específicas no desenvolvimento móvel: testar multithreading, verificar estado de Flow e mocking parcial de objetos reais.
Spy (ou mock parcial) permite criar um objeto que delega chamadas à implementação real mas permite sobrescrever métodos individuais. No MockK, spyk é criado com base em uma instância de classe real: val repo = spyk(InMemoryUserRepository()). Chamadas com expectativas definidas via every passam pelo Mock; o restante passa pelo objeto real. Spy é especialmente útil para testar código legado onde a injeção de dependência ainda não foi implementada e você só precisa sobrescrever um método.
Em projetos Android modernos usando Jetpack Compose, o ViewModel expõe estado através de StateFlow. O MockK permite mockar dependências Flow, e a biblioteca Turbine simplifica a verificação de emissões. O padrão clássico: MockK para um UseCase que retorna um Flow, Turbine para verificar as emissões do ViewModel. Este stack é recomendado pela documentação de Android Testing (Google, 2024) para projetos com Kotlin Coroutines.
class SearchViewModelTest {
private val searchUseCase = mockk<SearchUseCase>()
private val vm = SearchViewModel(searchUseCase)
fun `search emits results`() = runTest {
coEvery { searchUseCase.search("android") } returns
flowOf(SearchResult.Success(listOf(Item("Android TDD"))))
vm.search("android")
vm.state.test {
val state = awaitItem()
assertTrue(state.items.isNotEmpty())
cancelAndIgnoreRemainingEvents()
}
}
}
Perguntas frequentes
Mock é um conceito, um tipo de Test Double que verifica comportamento. Mockito é uma biblioteca para criar objetos Mock em Java e Android. Outras bibliotecas: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).
Para testar funções suspend com Mock, use MockK (coEvery / coVerify) ou Mockito com mockito-kotlin. O MockK suporta corrotinas nativamente: coEvery define o comportamento de uma função suspend, coVerify verifica sua chamada dentro de uma corrotina. Todas as chamadas suspend devem ser executadas dentro de runTest (kotlinx-coroutines-test).
Sim. No MockK, use returnsMany: every { api.getData() } returnsMany listOf(response1, response2). No Mockito — uma cadeia de thenReturn(value1).thenReturn(value2). Isso é útil para testar comportamento com chamadas sequenciais retornando respostas diferentes.
No MockK, use a anotação @MockK com relaxed = true e chame clearMocks(mock) no método @After. No Mockito — Mockito.reset(mock). Melhor prática: criar um novo Mock para cada teste via @Before para eliminar interferência entre testes.
MockK funciona corretamente com classes seladas: every { useCase() } returns Result.Success(data). Mockito não suporta classes seladas diretamente, exigindo soluções alternativas. Esta é uma razão pela qual o MockK é recomendado para projetos Kotlin em vez do Mockito.
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