Test Doubles — o que são, tipos de substitutos e aplicação

Autor: IT Sectr Publicado: 2026-04-10 Tempo de leitura: 9 min

Test Doubles são objetos substitutos usados em testes unitários no lugar de dependências reais. O termo foi introduzido por Gerard Meszaros no livro “xUnit Test Patterns” (2007) como um conceito geral para Mock, Stub, Fake, Spy e Dummy. De acordo com Martin Fowler (2024), os Test Doubles permitem isolar o componente sob teste do seu ambiente, tornando os testes determinísticos, rápidos e independentes de serviços externos.

Principais pontos

  • Test Doubles — termo geral para todos os tipos de objetos substitutos em testes
  • Mock verifica a interação: quais métodos foram chamados e com quais argumentos
  • Stub retorna valores predefinidos sem verificar chamadas
  • Fake — implementação funcional simplificada (ex.: banco de dados em memória)
  • Spy registra chamadas para verificação posterior, Dummy preenche parâmetros

O que são Test Doubles?

Test Doubles é um termo da indústria automotiva (dublê), transferido para o desenvolvimento de software. Assim como um dublê substitui um ator em uma cena perigosa, um Test Double substitui um componente real em um cenário de teste. Isso é necessário quando a dependência real está indisponível, é lenta, não determinística ou tem efeitos colaterais.

O conceito de Test Double abrange cinco tipos específicos, cada um resolvendo sua própria tarefa. A tipologia de Meszaros é canônica e usada em todos os guias modernos de teste. A diferença entre os tipos está no grau de controle e verificação: desde o simples preenchimento de parâmetros (Dummy) até a verificação completa da sequência de chamadas (Mock).

Por que os Test Doubles são necessários

O principal propósito dos Test Doubles é isolar o módulo sob teste. No desenvolvimento mobile, as dependências reais incluem servidores de API, bancos de dados, sistema de arquivos, sensores do dispositivo e serviços do sistema (LocationManager, Camera, Bluetooth). Usar esses componentes diretamente torna os testes lentos, frágeis e dependentes do ambiente. De acordo com o Google Testing Blog (2023), testes unitários bem isolados são executados em milissegundos, enquanto testes de integração são executados em segundos e minutos.

Cinco tipos de Test Doubles

A classificação de Gerard Meszaros inclui cinco tipos de Test Doubles, que diferem em comportamento e propósito. Entender a diferença entre eles é a base de testes unitários competentes.

Dummy

Dummy é um objeto que é passado para o método sob teste mas nunca é usado. Dummy é necessário apenas para satisfazer a assinatura do método. Em Kotlin, isso é frequentemente null, emptyList() ou um objeto com stubs. Dummy não deve conter nenhuma lógica — se for chamado, o teste deve falhar.

Fake

Fake é uma implementação simplificada mas funcional de uma interface. Ao contrário de Mock e Stub, Fake contém lógica de negócio real, mas de forma simplificada. Um exemplo clássico é o InMemoryUserRepository, que armazena dados em um HashMap em vez de um banco de dados. Fake é usado quando você precisa testar lógica que depende de estado, mas sem a sobrecarga da infraestrutura real.

TipoPropósitoExemplo
DummyPreencher um parâmetronull, objeto vazio
FakeImplementação simplificada funcionalInMemoryRepository
StubRetornar um valor fixowhen(api.getUser()).thenReturn(user)
SpyRegistrar chamadas para verificaçãoverify(spy).save(user)
MockVerificar interaçãoverify(mock).sendEmail(email)

Stub

Stub retorna valores predefinidos para chamadas específicas. Stub não verifica se foi chamado — ele simplesmente fornece dados. Em Mockito, Stub é criado via when(method).thenReturn(value). Stub é ideal para testes quando você precisa que uma dependência retorne um valor específico, mas o fato da chamada em si não é importante.

Spy

Spy é um invólucro em torno de um objeto real que registra todas as chamadas para verificação posterior. Ao contrário de Mock, Spy delega chamadas ao objeto real mas permite verificar que elas ocorreram. Em Mockito, Spy é criado via spy(realObject). Spy é útil para mocking parcial, quando você quer usar um objeto real mas verificar algumas chamadas.

Mock

Mock é um objeto com expectativas de chamada predefinidas. Mock verifica que métodos específicos foram chamados com argumentos específicos e em uma ordem específica. Ao contrário de Stub, Mock foca na verificação de comportamento em vez do retorno de dados. Mock é o tipo mais poderoso e mais usado de Test Double no desenvolvimento mobile.

Mock vs Stub: diferenças principais

A diferença entre Mock e Stub frequentemente causa confusão até mesmo entre desenvolvedores experientes. A principal diferença está no propósito: Stub verifica estado (state verification), Mock verifica comportamento (behavior verification).

Stub responde à pergunta: “o código retornou o resultado correto?”. Mock responde à pergunta: “o código chamou os métodos corretos com os argumentos corretos?”. No desenvolvimento mobile, Stub é usado quando o resultado importa (ex.: dados de um repositório), enquanto Mock é usado quando os efeitos colaterais importam (ex.: enviar email, escrever em um banco de dados).

kotlin
// Stub: verificação de estado
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: verificação de comportamento
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

Exemplos de Test Doubles em Kotlin

Exemplos práticos de todos os cinco tipos de Test Doubles em Kotlin usando MockK — a biblioteca de mocking mais popular para projetos Android.

Fake: InMemoryUserRepository

kotlin
class InMemoryUserRepository : UserRepository {
    private val store = mutableMapOf<String, User>()

    override fun save(user: User) {
        store[user.email] = user
    }

    override fun findByEmail(email: String): User? {
        return store[email]
    }
}

Stub + Mock: teste de UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: retornar resposta fixa de API
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: verificar que o usuário foi salvo
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: teste com parâmetro não utilizado

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: Context não é usado dentro de Logger
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

Quando usar cada tipo no desenvolvimento mobile

A escolha do tipo de Test Double depende do que exatamente está sendo testado: estado, comportamento ou integração. No desenvolvimento mobile Android e iOS, as seguintes recomendações foram estabelecidas.

Para ViewModel e UseCase

Ao testar ViewModel, use Mock para dependências que produzem efeitos colaterais (repositórios, analytics, navegação) e Stub para dependências que retornam dados (clientes de API, ContentProvider). Isso permite verificar que a ViewModel lida corretamente com cenários de sucesso e erro.

Para Repository e camada de dados

No nível de Repository, prefira Fake (implementações de banco de dados em memória) e Stub (respostas fixas de API). Fake permite testar lógica de cache e modo offline sem configurar SQLite. Stub simula vários status HTTP: 200, 404, 500, timeout.

  • Testes unitários de lógica de negócio — Mock para todas as dependências externas, Dummy para parâmetros não utilizados
  • Testes de integração — Fake em vez de Mock (verificar que os componentes funcionam juntos)
  • Testes de UI — Stub para respostas de API (via MockWebServer ou WireMock)
  • Testes de cache — Fake para banco de dados (em memória em vez de Room/SQLite)
  • Testes de assincronia — Mock com suporte a corrotinas (MockK + Turbine para Flow)

Erros comuns ao usar substitutos

O uso incorreto de Test Doubles é uma das causas mais comuns de testes frágeis que quebram a cada refatoração.

Over-mocking: uso excessivo de Mock

O erro mais comum é mockar tudo. Se cada dependência em um teste é substituída por um Mock, o teste para de verificar o comportamento real. Mock deve ser usado apenas para dependências externas (rede, banco de dados, sistema de arquivos, serviços do sistema). Componentes internos da aplicação (Value Object, data class, utilitários simples) não devem ser substituídos.

Under-specification: especificação insuficiente

O segundo erro é criar um Mock sem definir expectativas. Se um método é chamado sem every / when, Mock retorna um valor padrão (null, 0, false). Isso pode levar a testes falso-positivos, onde Mock silenciosamente retorna null e o teste interpreta isso como comportamento correto.

Over-verification: verificação excessiva

O terceiro erro é verificar cada chamada de cada Mock. Verify deve ser usado apenas para chamadas que são criticamente importantes do ponto de vista da lógica de negócio. A verificação excessiva torna os testes frágeis: alterar a ordem das chamadas no código de produção quebra os testes sem alterar o comportamento.

Perguntas frequentes

Qual é a diferença entre Mock e Stub?

Stub retorna dados e verifica o estado (o que foi retornado), enquanto Mock verifica o comportamento (quais métodos foram chamados). Stub = “retorne X”, Mock = “verifique que Y foi chamado com o argumento Z”. Em testes reais, um mesmo objeto frequentemente atua como Stub e Mock simultaneamente.

Quando usar Fake em vez de Mock?

Fake é preferível a Mock quando se testa lógica que depende de estado: cache, modo offline, transações. Fake (implementação em memória) permite testar esses cenários sem chamadas verify frágeis. Mock é mais adequado para verificar envio de dados: analytics, push, email.

Qual biblioteca de Test Doubles é melhor para Android?

Para projetos Android em Kotlin, MockK é recomendado. Ele suporta corrotinas, funções suspend, classes seladas e funções de extensão sem configuração adicional. Para projetos Java, Mockito continua sendo o padrão — a biblioteca mais popular com documentação extensa.

Como testar Kotlin Flow com Test Doubles?

Para testar Kotlin Flow, use a biblioteca Turbine junto com MockK. Turbine simplifica a verificação de emissões de Flow: você pode verificar a ordem dos valores, a conclusão do fluxo e exceções. Stub para Flow retorna flowOf(value), Mock verifica que o Flow foi coletado.

É aceitável usar Test Doubles em testes de UI?

Sim, mas no nível de respostas de API, não de componentes de UI. As bibliotecas MockWebServer (OkHttp) e WireMock permitem simular respostas HTTP em testes de UI. Os próprios componentes de UI (Compose, SwiftUI Views) não devem ser substituídos — seu comportamento é testado através de testes de captura de tela e Espresso.

Resumo

  • Test Doubles — termo geral para cinco tipos de objetos substitutos: Mock, Stub, Fake, Spy, Dummy
  • Mock verifica comportamento (verify), Stub retorna dados (thenReturn), Fake funciona como uma implementação real simplificada
  • Spy envolve um objeto real e registra chamadas, Dummy preenche parâmetros não utilizados
  • A tipologia de Gerard Meszaros é a classificação canônica usada em todos os frameworks modernos de mocking
  • Para projetos Kotlin, MockK é recomendado, para Java — Mockito, para iOS — Cuckoo ou OHHTTPStubs
  • Erros comuns: over-mocking (substituir tudo), under-specification (expectativas indefinidas), over-verification (verify excessivo)
  • Fake é preferível a Mock ao testar lógica com estado — cache, modo offline e transações

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.

Discutir o projeto

Leia também