Stub (stub, dublê de teste) — um objeto de teste que retorna respostas predefinidas para chamadas de método em vez de uma implementação real. No desenvolvimento móvel, stubs isolam o módulo testado de requisições de rede, bancos de dados e sistemas de arquivos, permitindo verificar a lógica sem configuração de ambiente. Ao contrário de mock, stub não verifica comportamento — ele apenas fornece dados. Mais detalhes no artigo de Martin Fowler sobre test doubles.
Principais pontos
Stub é um objeto dublê de teste que substitui uma dependência real em um teste e retorna valores predefinidos para chamadas específicas. O termo foi introduzido na classificação de Gerard Meszaros (2007) no livro «xUnit Test Patterns». Stub pertence à categoria de test doubles — objetos que substituem componentes reais durante os testes. O objetivo principal de um stub é fornecer à unidade testada dados previsíveis, eliminando a incerteza dos sistemas externos.
Como funciona — o teste configura o stub antes da execução: «quando o método getUsers() for chamado, retorne esta lista de usuários». Stub não contém lógica de negócio, não verifica a ordem das chamadas nem registra o histórico. Ele simplesmente fica no lugar do componente real e retorna o que foi instruído. No contexto de testes Android, isso significa: o cliente OkHttp não faz uma requisição real ao servidor, mas recebe uma resposta do MockWebServer configurado como stub.
Quando usar — stubs são ideais para testar a camada de UI (ViewModel, Presenter) e lógica de negócio (UseCase, Interactor), onde é necessário verificar a reação a dados específicos: lista vazia, servidor retornou erro 500, token expirado. Qualquer caso onde o teste exija um estado de entrada específico é tarefa para stub. Cada cenário de teste tem sua própria configuração de stub, tornando os testes legíveis e previsíveis.
Gerard Meszaros (2007) no livro «xUnit Test Patterns» identificou cinco tipos de test doubles: dummy, stub, spy, mock, fake. Cada tipo resolve seu problema. Dummy — é passado mas não usado. Stub — retorna dados. Spy — registra chamadas. Mock — verifica comportamento. Fake — contém lógica simplificada. Compreender esta classificação ajuda o desenvolvedor a escolher a ferramenta certa para cada cenário de teste.
Requisições de rede — o cenário mais comum para uso de stubs. O aplicativo faz chamadas HTTP à API, e o teste precisa verificar a reação a diferentes respostas: JSON bem-sucedido, erro 401 (não autorizado), tempo limite, array vazio. MockWebServer (OkHttp) no Android e URLProtocol (iOS) atuam como stubs, retornando respostas HTTP predefinidas sem conexão real com o servidor. Isso acelera os testes de segundos para milissegundos.
Banco de dados — Room (Android) e CoreData (iOS) têm variantes in-memory, mas sua configuração ainda leva tempo. Stub em vez do repositório retorna listas de Entity preparadas sem tocar no BD. Isso é especialmente eficaz para testar ViewModel, onde é necessário verificar ordenação, filtragem ou transformação de dados. O teste executa em milissegundos independentemente do volume de dados.
Serviços do sistema — LocationManager, SensorManager, SharedPreferences exigem um dispositivo real ou emulador. Stub para LocationProvider retorna coordenadas predefinidas, para SensorManager — valores fixos do acelerômetro. No iOS, o equivalente é CLLocationManager com uma implementação de delegado de teste. Sem stubs, esses testes exigem um dispositivo físico com condições específicas.
Sistema de arquivos e cache — carregamento de imagens, cache de respostas, trabalho com arquivos de configuração — todas essas operações dependem do estado do disco. Stub para FileManager ou ImageCache retorna sucesso/erro sem ler arquivos reais. Isso elimina falhas falsas em testes devido a discrepâncias de caminhos ou direitos de acesso em diferentes máquinas de desenvolvimento.
Divisão de responsabilidades — três tipos de test doubles resolvem tarefas diferentes. Stub: «me dê dados». Mock: «verifique que fui chamado». Fake: «funciono como o real, só que mais simples». A diferença é crítica para a legibilidade dos testes: se um teste usa mock onde precisa de stub, ele fica sobrecarregado com chamadas verify não relacionadas ao cenário testado.
| Característica | Stub | Mock | Fake |
|---|---|---|---|
| Propósito | Fornecer dados | Verificar interação | Implementação simplificada |
| Lógica | Não | Não | Sim (mas simplificada) |
| Verificação | Não | Sim (verify) | Indireta (via estado) |
| Flexibilidade | Baixa — respostas fixas | Média | Alta — lógica adapta |
| Velocidade | Máxima | Alta | Média |
| Exemplo | MockWebServer retorna JSON | Mockito.verify(repository).save() | InMemoryRepository com HashMap |
Regra prática — se o teste verifica quais dados o componente testado recebeu — use stub. Se o teste verifica se o componente chamou o método da dependência com os argumentos corretos — use mock. Se você simplesmente quer substituir um BD por uma tabela hash — isso é fake. Misturar tipos em um mesmo teste o torna frágil: ao mudar a implementação, você terá que reescrever tanto a lógica de stub quanto de verify.
Stub com verify — um erro comum onde o desenvolvedor configura um stub e depois adiciona verify(stub).method(). Stub por definição não deve ser verificado — para isso existe mock. Se você precisa verificar que um método foi chamado com argumentos específicos, use Mockito.mock() em vez de Mockito.stub(). Essa separação mantém a intenção do teste clara para outros desenvolvedores.
MockWebServer — uma biblioteca OkHttp para criar stubs HTTP no Android e JVM. Inicia um servidor HTTP local em uma porta especificada que intercepta as requisições do cliente OkHttp e retorna respostas predefinidas. A configuração leva três linhas: criar o servidor, enfileirar a resposta, iniciá-lo. O teste pode enfileirar sequencialmente várias respostas para cenários com paginação ou tentativas.
class UserRepositoryTest {
private val server = MockWebServer()
fun setup() {
server.start(8080)
val client = OkHttpClient.Builder()
.readTimeout(1, TimeUnit.SECONDS)
.build()
}
fun test_user_list_success() {
val json = "[{\"id\":1,\"name\":\"Alice\"}]"
server.enqueue(MockResponse()
.setBody(json)
.setResponseCode(200)
)
val result = repository.getUsers()
assertEquals(1, result.size)
}
fun teardown() {
server.shutdown()
}
}
MockK — uma alternativa ao Mockito para Kotlin com suporte de primeira classe para corrotinas, funções de extensão e classes seladas. Stubs no MockK são criados via coEvery (para funções suspend) e every (para funções regulares). Ao contrário do MockWebServer, o MockK stub métodos de dependência individuais em vez de toda a camada HTTP. Isso é útil para testes unitários de UseCase ou Interactor, onde as dependências são abstrações de repositórios.
interface UserRepository {
suspend fun getUsers(): List<User>
}
class GetUsersUseCaseTest {
private val repo = mockk<UserRepository>()
private val useCase = GetUsersUseCase(repo)
fun test_empty_list() = runTest {
coEvery { repo.getUsers() } returns emptyList()
val result = useCase.invoke()
assertTrue(result.isEmpty())
coVerify(exactly = 1) { repo.getUsers() }
}
}
Melhores práticas — para testes de integração use MockWebServer (intercepta HTTP real), para testes unitários use MockK (stub interfaces). Não stube o que você não está testando: se o teste verifica o Repository, não stube o cliente OkHttp dentro dele — use um MockWebServer real no nível HTTP. Essa regra mantém os testes relevantes e reduz a fragilidade durante a refatoração.
Protocolos Swift como stubs — na abordagem nativa do iOS, o stub é implementado substituindo uma estrutura de teste que está em conformidade com o protocolo da dependência. Em vez de um NetworkService real, o teste recebe um StubNetworkService que retorna dados fixos. Swift é uma linguagem com tipagem estática, portanto o stub deve estar em conformidade com o mesmo protocolo que o serviço real. O compilador garante que o stub implementa todos os métodos necessários.
protocol NetworkServiceProtocol {
func fetchUsers() async throws -> [User]
}
struct StubNetworkService: NetworkServiceProtocol {
let result: Result<[User], Error>
func fetchUsers() async throws -> [User] {
try result.get()
}
}
final class UsersViewModelTests: XCTestCase {
func test_success_state() async {
let stub = StubNetworkService(
result: .success([User(name: "Alice")])
)
let vm = UsersViewModel(service: stub)
await vm.load()
XCTAssertEqual(vm.users.count, 1)
}
}
OCMock para Objective-C — uma biblioteca para criar stubs e mocks em projetos iOS legados. OCMock suporta métodos stub com argumentos e valores de retorno. Projetos modernos em Swift preferem uma abordagem baseada em protocolos com stubs manuais — isso dá controle sobre cada método e não requer dependências externas. OCMock continua sendo uma opção para projetos onde protocolizar todas as dependências não é economicamente viável.
URLProtocol para stubs HTTP — um mecanismo do sistema no iOS para interceptar requisições de rede através de uma subclasse de URLProtocol. O teste registra um URLProtocol personalizado que intercepta URLSession e retorna respostas stub. A vantagem sobre stubs manuais: você não precisa mudar a arquitetura do aplicativo — URLSession permanece real, mas os dados são substituídos no nível do protocolo. A desvantagem: é mais difícil de depurar do que um serviço stub explícito.
Perguntas frequentes
Stub retorna dados predefinidos e não verifica se uma chamada ocorreu. Mock adicionalmente verifica que o método foi chamado com os argumentos corretos (verify). Stub responde à pergunta «o que retornar», Mock responde a «a chamada foi feita». Use stub para verificação de estado, mock para verificação de interação.
Fake é necessário quando o teste requer uma implementação funcional (embora simplificada) — por exemplo, um banco de dados in-memory em vez de Room. Stub é adequado para cenários únicos com dados predefinidos. Se você repete o mesmo stub em 10 testes — provavelmente você precisa de um Fake. Fake reduz a duplicação porque a lógica vive em uma única classe.
No Android — MockK para objetos Kotlin (object) suporta mockkObject(), incluindo métodos estáticos de classes Java via mockkStatic(). No iOS — métodos estáticos do Swift não podem ser stubados diretamente; use protocolos e DI para substituir uma chamada static por um método de instância de um protocolo. Stubs estáticos são débito técnico e devem ser evitados em código novo.
Use MockWebServer (OkHttp) — funciona como um servidor HTTP local que enfileira respostas. Para Retrofit, basta substituir a URL base por localhost:8080. Para Ktor, use MockEngine — um mecanismo integrado para substituir HttpStatement. Ambas as abordagens funcionam sem internet real e dão controle total sobre o código de status, corpo e cabeçalhos da resposta.
Spy envolve um objeto real e registra chamadas, enquanto Stub substitui completamente o objeto por respostas fixas. Spy permite o uso parcial da implementação real (outros métodos funcionam como estão), enquanto stub não. Se você precisa verificar que um método foi chamado mas parte da lógica deve ser executada — use spy, não stub.
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