A pirâmide e as camadas
A divisão mais difundida de testes é por escopo: o quanto do sistema um teste exercita de uma vez. Um teste de unidade verifica uma peça pequena e isolada, uma função, um método, uma classe, sem sair dela. Um teste de integração verifica que duas ou mais peças conversam como deveriam, o código com o banco, um serviço com outro, a aplicação com uma biblioteca. Um teste ponta a ponta (ou end-to-end, e2e) exercita o sistema inteiro pela borda que o usuário toca, subindo tudo e clicando na interface como um usuário faria. Cada degrau abraça mais do sistema, e é essa diferença de escopo que governa tudo o que segue.
Antes de arranjar essas camadas numa forma, convém admitir que as fronteiras entre elas são borradas. Martin Fowler é explícito ao notar que “teste de unidade” e “teste de integração” não têm definição uniforme: o que uma equipe chama de unidade, outra chama de integração, e o desacordo sobre proporções muitas vezes é, no fundo, um desacordo sobre vocabulário. Vale ter isso em mente antes de levar qualquer receita de proporção ao pé da letra.
Solitário ou sociável
Section titled “Solitário ou sociável”Dentro do próprio teste de unidade há uma bifurcação que decide muita coisa. Quando a unidade sob teste depende de colaboradores (outras classes, o relógio, a rede), há duas posturas. Ou se isola a unidade, substituindo cada colaborador por um dublê de teste (um mock, um stub), e assim se testa a peça sozinha; ou se deixa a unidade usar seus colaboradores reais, testando-a acompanhada. Jay Fields batizou as duas de teste solitário e teste sociável.
Essa escolha define duas escolas, que Fowler descreve como mockista e classicista. O mockista insiste no teste solitário: cada unidade testada em completo isolamento, colaboradores sempre substituídos por dublês. O classicista prefere o sociável, usa dublês só quando o colaborador real é inconveniente (lento, não determinístico, com efeito colateral caro) e deixa o resto conversar de verdade. O próprio Fowler se declara classicista, e a razão informa todo o resto da discussão sobre mocks: um teste solitário é mais rápido e aponta o defeito com mais precisão, mas paga isso testando a unidade contra a sua suposição de como o colaborador se comporta, não contra o colaborador de fato.
Por que uma pirâmide
Section titled “Por que uma pirâmide”O arranjo dessas camadas numa pirâmide é atribuído a Mike Cohn, que o descreveu no livro Succeeding with Agile de 2009, tendo esboçado a ideia em conversa com Lisa Crispin por volta de 2003. A prescrição é geométrica: muitos testes de unidade na base, menos testes de integração no meio, poucos testes ponta a ponta no topo. E a justificativa de Cohn para empurrar o volume para baixo é direta, nas palavras que Fowler cita: testes que rodam ponta a ponta pela interface são frágeis, caros de escrever e demorados de rodar.
A pirâmide, portanto, não é uma estética. Ela é o desenho que cai naturalmente quando se leva a sério a régua de confiança sobre custo, camada por camada. O que muda entre os degraus são quatro grandezas ao mesmo tempo, e é a interação delas que dá o formato:
| Camada | Velocidade | Custo de escrita e manutenção | Fidelidade ao sistema real | Precisão do diagnóstico |
|---|---|---|---|---|
| Unidade | milissegundos | baixo, mas cresce com o volume | baixa: testa a peça, não o todo | alta: aponta a função exata |
| Integração | dezenas a centenas de ms | médio: precisa de infra (banco, filas) | média: testa peças reais conversando | média: aponta a costura, não a linha |
| Ponta a ponta | segundos a minutos | alto: frágil, lento, propenso a flakiness | alta: exercita o que o usuário vê | baixa: falhou em algum lugar do todo |
A leitura da tabela é a leitura da pirâmide. Testes de unidade são baratos de rodar e cirúrgicos no diagnóstico, então cabem aos milhares e formam a base larga; o preço que pagam é a fidelidade, porque uma peça que passa sozinha ainda pode falhar quando encaixada nas outras. Testes ponta a ponta são o oposto: exercitam o sistema como ele de fato roda, o que dá a maior confiança de que o caminho crítico funciona, mas custam caro em cada eixo, são lentos, quebram por motivos alheios ao código (uma animação, um timeout, um dado de ambiente) e, quando quebram, dizem apenas que algo em algum lugar falhou. Por isso são poucos, no topo estreito, reservados aos poucos fluxos que doem mais se quebrarem.
O eixo que a tabela não mostra, mas que amarra tudo, é o ciclo de feedback. Uma suíte de unidade roda em segundos e pode ser executada a cada tecla; uma suíte ponta a ponta roda em minutos e é executada de hora em hora. Quanto mais cedo e mais rápido o teste avisa, mais barato é o defeito que ele pega, porque o contexto ainda está fresco na cabeça de quem escreveu. Empurrar o volume para a base é, em boa medida, comprar feedback rápido.
O cone de sorvete
Section titled “O cone de sorvete”O anti-padrão que espelha a pirâmide é o cone de sorvete: a pirâmide de cabeça para baixo, com uma multidão de testes ponta a ponta (muitas vezes manuais) no topo largo, alguns de integração no meio e quase nada de unidade na ponta fina embaixo. Ele surge sem que ninguém o decida, e é o resultado natural de testar sempre pela interface, que é onde o comportamento é mais visível. O custo aparece depois: a suíte fica lenta ao ponto de rodar uma vez por dia, frágil ao ponto de ninguém confiar quando ela fica vermelha, e cara ao ponto de virar gargalo. O cone de sorvete não é uma escolha, é uma dívida que se acumula quando se adia escrever teste de unidade.
O preço dos mocks
Section titled “O preço dos mocks”O mock é o instrumento que torna o teste solitário possível, e entender o que ele cobra é entender metade dos debates sobre camadas. Substituir um colaborador por um dublê compra velocidade e isolamento: o teste não precisa subir um banco nem falar com a rede, roda em milissegundos e, quando falha, o defeito está na unidade, não no vizinho. O que ele cobra é fidelidade e acoplamento. Fidelidade, porque o teste passa a verificar o comportamento contra a sua imagem do colaborador, e se essa imagem estiver errada (o banco devolve outra coisa, a API mudou o formato), o teste continua verde enquanto o sistema real quebra. Kent C. Dodds resume o custo numa frase: quando você mocka algo, remove toda a confiança na integração entre o que está testando e o que foi mockado. Acoplamento, porque um teste cheio de mocks conhece a estrutura interna da unidade (quem ela chama, em que ordem) e passa a quebrar a cada refatoração, mesmo quando o comportamento observável não mudou. Mock demais transforma a suíte de rede de segurança em cimento.
As rivais da pirâmide
Section titled “As rivais da pirâmide”A pirâmide reinou por mais de uma década, mas a partir do fim dos anos 2010 duas formas rivais apareceram, e a discórdia entre elas não é estética: é sobre em qual camada a razão confiança sobre custo é de fato melhor.
O troféu de testes é de Kent C. Dodds, que o propôs em 2018 a partir de um tweet de Guillermo Rauch de dezembro de 2016, “Write tests. Not too many. Mostly integration”. O troféu tem quatro níveis: análise estática na base (linter, tipos), depois unidade, depois integração como a faixa mais larga, e ponta a ponta no topo. O deslocamento em relação à pirâmide é o meio inchado: a aposta é que, no mundo do front-end JavaScript, a integração é onde cada teste rende mais confiança por real gasto. O princípio que Dodds usa para justificar isso vale ser guardado, porque reaparece na discussão de mocks e de ferramentas modernas:
Quanto mais seus testes se parecem com a forma como seu software é usado, mais confiança eles podem te dar.
O favo de mel vem da engenharia da Spotify, num texto de 2018 sobre testar microsserviços, e é ainda mais agressivo: muitos testes de integração no meio largo, poucos testes de detalhe de implementação, e o mínimo possível (idealmente nenhum) de testes que dependem de outros sistemas de verdade. O argumento é que, num microsserviço, a maior complexidade não está dentro do serviço, e sim em como ele conversa com os outros, de modo que a pirâmide clássica não só deixa de servir como pode ser “ativamente prejudicial”, empurrando esforço para a base (unidade) onde o risco não está.
Qual forma seguir
Section titled “Qual forma seguir”Fowler, num texto de 2021 sobre as “diversas e fantásticas formas de teste”, enquadra pirâmide, favo de mel e troféu como imagens que servem para indicar quanto esforço gastar em cada tipo de teste, sobretudo no equilíbrio entre unidade e testes mais amplos. Mas o próprio Fowler é cético quanto à disputa: como “unidade” e “integração” não têm definição fixa, boa parte do debate sobre proporções fica sem chão, e ele endossa a observação de Justin Searls de que discutir proporção é uma distração diante do que importa, a qualidade de cada teste.
A síntese honesta, então, não é escolher uma forma e segui-la como dogma. É reconhecer que a forma certa é a que espelha o perfil de custo e de risco do seu sistema. Um monólito com lógica de domínio rica tem seu risco concentrado nessa lógica, e uma base larga de unidade paga bem. Um microsserviço que é quase só cola entre outros serviços tem o risco na costura, e aí o favo de mel descreve melhor onde gastar. O invariante por trás de todas as formas é o mesmo, e é ele, não a proporção, que vale internalizar: empurre cada teste para a camada mais baixa em que ele ainda exercite algo que possa de fato quebrar, e deixe o risco, não a geometria, dizer onde essa camada fica.
Palavras-chave
Section titled “Palavras-chave”Camadas: teste de unidade, teste de integração, teste ponta a ponta, teste de sistema, análise estática Formas: pirâmide de testes, cone de sorvete, troféu de testes, favo de mel Conceitos: teste solitário, teste sociável, escola mockista, escola classicista, dublê de teste, mock, stub, fidelidade, ciclo de feedback, flakiness Pessoas e fontes: Mike Cohn, Lisa Crispin, Martin Fowler, Kent C. Dodds, Guillermo Rauch, Justin Searls, Jay Fields, engenharia da Spotify