
Software Engineer
Engenheiro de software especializado em testes automatizados e qualidade de código. Conecta disciplina técnica com entrega de produtos confiáveis. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Una lista de 47 puntos para encontrar errores, riesgos de seguridad y problemas de rendimiento antes del lanzamiento.
Templates probados en producción, usados por desarrolladores. Ahorra semanas de setup en tu próximo proyecto.

Semana passada, um time com 94% de cobertura de testes teve um bug critico em producao. Todos os 847 testes passaram. A pipeline estava verde. O deploy foi automatico. E a aplicacao quebrou para 40% dos usuarios.
O problema nao era a falta de testes. Era o tipo de testes.
A IA havia gerado o codigo de uma funcao de validacao de pagamento. O desenvolvedor pediu que a mesma IA escrevesse os testes. Ela escreveu 23 testes unitarios, todos passando. Cobertura de linha: 100%. Cobertura de branch: 98%.
Mas nenhum teste validava o comportamento real. Todos espelhavam a implementacao. Quando a funcao fazia if (amount > 0), o teste verificava exatamente isso: expect(validate(100)).toBe(true). Quando a funcao fazia if (!cardNumber.startsWith('4')), o teste verificava exatamente isso. Era um eco, nao uma validacao.
Isso e o que chamo de teste tautologico: um teste que repete a implementacao em vez de validar o comportamento esperado. E quando a IA escreve tanto o codigo quanto os testes, testes tautologicos se tornam a regra, nao a excecao.
A suite verde mais perigosa e aquela onde a IA escreveu o codigo e os testes. Quem vigia o vigilante?
Este artigo apresenta uma estrategia completa para testes na era da IA. Nao e sobre "como usar IA para gerar testes mais rapido." E sobre como garantir que testes — gerados por humanos ou por IA — realmente funcionam. Voce vai aprender a Nova Piramide de Testes para 2026, o framework TRUST para decidir quando confiar em testes de IA, e tecnicas como property-based testing e mutation testing que sao sua defesa contra a falsa confianca de suites verdes que nao testam nada real.
Antes de propor solucoes, precisamos entender o problema em profundidade. Testes tautologicos nao sao novos — desenvolvedores humanos tambem os escrevem. Mas a IA amplifica esse anti-pattern de forma sistematica.
Considere esta funcao de calculo de desconto:
// Implementacao gerada por IA
function calculateDiscount(price: number, customerType: string): number {
if (customerType === 'premium') {
return price * 0.2;
}
if (customerType === 'regular') {
return price * 0.1;
}
return 0;
}
A IA, quando solicitada a escrever testes, gera:
// Testes gerados pela mesma IA — tautologicos
describe('calculateDiscount', () => {
it('should return 20% for premium customers', () => {
expect(calculateDiscount(100, 'premium')).toBe(20);
});
it('should return 10% for regular customers', () => {
expect(calculateDiscount(100, 'regular')).toBe(10);
});
it('should return 0 for unknown customer types', () => {
expect(calculateDiscount(100, 'unknown')).toBe(0);
});
});
Esses testes passam. A cobertura e 100%. Mas eles nao testam nada que a implementacao ja nao tenha definido. Sao uma repeticao, nao uma validacao.
O que esses testes NAO verificam:
price negativo?price zero?customerType em maiusculas ('PREMIUM')?customerType nulo ou undefined?A IA comete esse erro por design, nao por acidente. Quando voce pede "escreva testes para esta funcao", a IA analisa o codigo e gera testes que espelham seu comportamento atual. Ela nao tem acesso a:
'Premium' com P maiusculo deveria funcionarA IA testa o que o codigo FAZ, nao o que o codigo DEVERIA fazer. E essa diferenca e a raiz de todos os problemas.
Um teste que espelha a implementacao nao e um teste. E um eco.
Testes tautologicos criam um custo triplo:
Falsa confianca: a equipe acredita que 94% de cobertura significa 94% de seguranca. Bugs passam para producao porque "os testes estao verdes."
Custo de manutencao sem valor: quando a implementacao muda, os testes quebram — mas nao porque detectaram um bug, e sim porque estao acoplados a detalhes internos. Voce gasta tempo atualizando testes que nunca protegeram nada.
Resistencia a refatoracao: desenvolvedores evitam melhorar o codigo porque cada mudanca quebra dezenas de testes. O codebase ossifica.
Equipes com testes tautologicos frequentemente tem mais bugs em producao do que equipes com menos testes, mas testes melhores. A metrica errada incentiva o comportamento errado.
A piramide de testes classica (muitos testes unitarios, menos testes de integracao, poucos testes E2E) foi projetada para um mundo onde:
Esse mundo nao existe mais. Com IA gerando codigo e testes em segundos, os trade-offs mudaram. A nova piramide adiciona camadas de verificacao que nao existiam antes.
/\
/ \
/ E2E \
/ + IA \
/ Visual \
/------------\
/ Integration \
/ + Contract \
/------------------\
/ Property-Based \
/ (Encontra Edge Cases)\
/------------------------\
/ Unit Tests \
/ (IA + Supervisao) \
/------------------------------\
/ Mutation Testing \
/ (Valida Eficacia da Suite) \
/------------------------------------\
Camada Base: Mutation Testing
Mutation testing nao e um tipo de teste — e uma meta-verificacao. Ele responde a pergunta: "meus testes realmente detectam bugs?" Se voce remover essa camada, todas as outras se tornam suspeitas.
Camada 1: Unit Tests com Supervisao
Testes unitarios continuam sendo a base, mas com uma diferenca crucial: todo teste gerado por IA passa por revisao humana antes de ser aceito. O humano valida se o teste testa comportamento ou apenas espelha implementacao.
Camada 2: Property-Based Testing
Esta camada e nova na piramide. Property-based testing gera centenas de inputs aleatorios para verificar propriedades invariantes do codigo. Encontra edge cases que nem humanos nem IA imaginam.
Camada 3: Integration + Contract Testing
Com APIs cada vez mais geradas por IA, contract testing se torna essencial. Valida que o contrato entre servicos se mantem, independente de como a implementacao foi gerada.
Camada 4: E2E + Visual com IA
Testes E2E tradicionais combinados com visual regression testing assistido por IA. A IA compara screenshots e identifica mudancas inesperadas, mas humanos decidem se sao bugs ou features.
Na piramide classica, a prioridade era velocidade: muitos testes rapidos (unitarios), poucos testes lentos (E2E).
Na nova piramide, a prioridade e confianca: a base e mutation testing que valida eficacia, seguido por property-based testing que encontra edge cases, e so entao os testes tradicionais.
Isso nao significa executar mutation testing em todo commit — seria lento demais. Significa que mutation testing define o padrao de qualidade que os outros testes devem atingir. E uma verificacao periodica, nao continua.
Property-based testing e a tecnica mais subutilizada em times de desenvolvimento. E tambem a mais eficaz contra os vicios da geracao de testes por IA.
Testes tradicionais usam exemplos: "dado input X, espero output Y." Property-based testing usa propriedades: "para QUALQUER input valido, esta invariante deve ser verdadeira."
Voltemos a funcao de desconto:
// Abordagem tradicional: exemplos especificos
it('should return 20 for premium with price 100', () => {
expect(calculateDiscount(100, 'premium')).toBe(20);
});
// Abordagem property-based: propriedade invariante
it('discount should never exceed original price', () => {
fc.assert(
fc.property(
fc.integer({ min: 0, max: 10000 }),
fc.constantFrom('premium', 'regular', 'basic'),
(price, customerType) => {
const discount = calculateDiscount(price, customerType);
return discount <= price && discount >= 0;
}
)
);
});
O teste com exemplos verifica um caso. O teste com propriedade verifica milhares de casos aleatorios contra uma invariante: "o desconto nunca pode ser maior que o preco original e nunca pode ser negativo."
fast-check e a biblioteca de property-based testing mais usada em TypeScript. Integra perfeitamente com Vitest:
import { describe, it, expect } from 'vitest';
import * as fc from 'fast-check';
// Funcao sob teste
function sortNumbers(arr: number[]): number[] {
return [...arr].sort((a, b) => a - b);
}
describe('sortNumbers - property-based', () => {
it('resultado deve ter o mesmo tamanho que a entrada', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = sortNumbers(arr);
return sorted.length === arr.length;
})
);
});
it('resultado deve conter os mesmos elementos', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = sortNumbers(arr);
const sumOriginal = arr.reduce((a, b) => a + b, 0);
const sumSorted = sorted.reduce((a, b) => a + b, 0);
return sumOriginal === sumSorted;
})
);
});
it('cada elemento deve ser menor ou igual ao proximo', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = sortNumbers(arr);
for (let i = 0; i < sorted.length - 1; i++) {
if (sorted[i] > sorted[i + 1]) return false;
}
return true;
})
);
});
it('ordenar duas vezes deve dar o mesmo resultado', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const once = sortNumbers(arr);
const twice = sortNumbers(once);
return JSON.stringify(once) === JSON.stringify(twice);
})
);
});
});
Cada propriedade testa uma invariante diferente. Juntas, elas garantem que a funcao de ordenacao esta correta para qualquer input — nao apenas para os exemplos que um humano ou IA imaginou.
O poder real do property-based testing aparece quando voce cria geradores customizados para seu dominio:
import * as fc from 'fast-check';
// Arbitrario para CPF valido (estrutura, nao validacao de digitos)
const cpfArbitrary = fc.tuple(
fc.stringOf(fc.constantFrom(...'0123456789'), { minLength: 3, maxLength: 3 }),
fc.stringOf(fc.constantFrom(...'0123456789'), { minLength: 3, maxLength: 3 }),
fc.stringOf(fc.constantFrom(...'0123456789'), { minLength: 3, maxLength: 3 }),
fc.stringOf(fc.constantFrom(...'0123456789'), { minLength: 2, maxLength: 2 })
).map(([a, b, c, d]) => `${a}.${b}.${c}-${d}`);
// Arbitrario para pedido de e-commerce
const orderArbitrary = fc.record({
id: fc.uuid(),
customerId: fc.uuid(),
items: fc.array(
fc.record({
productId: fc.uuid(),
quantity: fc.integer({ min: 1, max: 100 }),
unitPrice: fc.integer({ min: 1, max: 100000 }), // centavos
}),
{ minLength: 1, maxLength: 20 }
),
discount: fc.integer({ min: 0, max: 5000 }), // centavos
createdAt: fc.date({ min: new Date('2020-01-01'), max: new Date() }),
});
// Propriedades de negocio
describe('Order calculations', () => {
it('total nunca deve ser negativo', () => {
fc.assert(
fc.property(orderArbitrary, (order) => {
const total = calculateOrderTotal(order);
return total >= 0;
})
);
});
it('total deve ser soma dos items menos desconto', () => {
fc.assert(
fc.property(orderArbitrary, (order) => {
const itemsTotal = order.items.reduce(
(sum, item) => sum + item.quantity * item.unitPrice,
0
);
const expectedTotal = Math.max(0, itemsTotal - order.discount);
const actualTotal = calculateOrderTotal(order);
return actualTotal === expectedTotal;
})
);
});
it('desconto nunca deve exceder o total dos items', () => {
fc.assert(
fc.property(orderArbitrary, (order) => {
const itemsTotal = order.items.reduce(
(sum, item) => sum + item.quantity * item.unitPrice,
0
);
// Esta propriedade pode FALHAR se a implementacao permitir
// desconto maior que o total — e isso e um bug!
return order.discount <= itemsTotal;
})
);
});
});
Property-based testing encontra os bugs que a IA sequer imaginou, porque ele testa premissas, nao cenarios.
Quando fast-check encontra um input que viola uma propriedade, ele automaticamente "encolhe" o input ate encontrar o caso minimo que ainda falha. Isso transforma um array de 50 elementos em um de 2, facilitando o debug.
it('demonstracao de shrinking', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
// Bug intencional: falha se array tem mais de 3 elementos
return arr.length <= 3;
})
);
// fast-check vai encontrar [0, 0, 0, 0] como caso minimo
// nao um array de 100 elementos aleatorios
});
Se property-based testing encontra edge cases, mutation testing responde uma pergunta diferente: "meus testes existentes realmente detectam bugs?"
Mutation testing injeta bugs artificiais (mutacoes) no codigo e verifica se os testes falham. Se uma mutacao passa despercebida, os testes nao estao testando aquele comportamento.
// Codigo original
function isAdult(age: number): boolean {
return age >= 18;
}
// Mutacao 1: troca >= por >
function isAdult_mutant1(age: number): boolean {
return age > 18; // Mutacao: >= virou >
}
// Mutacao 2: troca 18 por 17
function isAdult_mutant2(age: number): boolean {
return age >= 17; // Mutacao: 18 virou 17
}
// Mutacao 3: inverte retorno
function isAdult_mutant3(age: number): boolean {
return age < 18; // Mutacao: >= virou <
}
Se seus testes passam com alguma dessas mutacoes, voce tem um problema. A mutacao deveria ter sido "morta" (detectada), mas "sobreviveu."
Stryker e o mutation testing framework mais maduro para JavaScript/TypeScript:
npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner
// stryker.config.mjs
export default {
packageManager: 'npm',
testRunner: 'vitest',
vitest: {
configFile: 'vitest.config.ts',
},
reporters: ['html', 'clear-text', 'progress'],
coverageAnalysis: 'perTest',
mutate: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts',
],
thresholds: {
high: 80,
low: 60,
break: 50, // Falha o build se mutation score < 50%
},
concurrency: 4,
timeoutMS: 10000,
};
Executar Stryker gera um relatorio assim:
Mutation testing report:
--------------------------------------------------
File | Score | Killed | Survived | No Coverage
--------------------------------------------------
src/validators.ts | 89% | 45 | 5 | 2
src/calculations.ts | 47% | 12 | 14 | 0
src/utils.ts | 92% | 23 | 2 | 0
--------------------------------------------------
Total | 73% | 80 | 21 | 2
Killed: mutacoes detectadas pelos testes (bom) Survived: mutacoes nao detectadas (ruim — seus testes nao cobrem esse comportamento) No Coverage: codigo sem nenhum teste
O calculations.ts com 47% de mutation score e um alerta vermelho. Metade das mutacoes sobreviveram, o que significa que metade dos comportamentos nao estao sendo testados de verdade.
Mutation testing e o unico jeito objetivo de responder: "meus testes realmente detectam bugs, ou apenas existem?"
Um mutation score baixo nao e um problema — e informacao. Use o relatorio HTML do Stryker para ver exatamente quais mutacoes sobreviveram:
// Stryker mostra: mutacao "troca >= por >" sobreviveu nesta linha
function calculateShipping(weight: number): number {
if (weight >= 10) { // <- mutacao sobreviveu aqui
return weight * 2;
}
return weight * 1.5;
}
// Teste existente (insuficiente)
it('should calculate shipping for heavy items', () => {
expect(calculateShipping(15)).toBe(30);
});
// Teste que mata a mutacao (adicionar)
it('should use higher rate exactly at 10kg threshold', () => {
expect(calculateShipping(10)).toBe(20); // Testa o limite exato
expect(calculateShipping(9.99)).toBe(14.985); // Testa abaixo do limite
});
Cada mutacao sobrevivente e um teste faltando. O relatorio do Stryker e um roteiro de melhoria.
Mutation testing e computacionalmente caro — pode levar 10-100x mais que a suite normal. Nao execute em todo commit. Estrategias praticas:
# GitHub Actions: mutation testing noturno
name: Mutation Testing
on:
schedule:
- cron: '0 3 * * *' # 3am UTC todo dia
workflow_dispatch: # Manual trigger
jobs:
mutation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx stryker run
- uses: actions/upload-artifact@v4
with:
name: mutation-report
path: reports/mutation/
IA pode e deve gerar testes — mas nunca sem supervisao. O problema nao e a ferramenta, e o processo.
A IA e excelente para:
Boilerplate de testes: setup, teardown, mocks, fixtures. A estrutura repetitiva que todo teste precisa.
// IA gera rapidamente:
describe('UserService', () => {
let service: UserService;
let mockRepository: jest.Mocked<UserRepository>;
beforeEach(() => {
mockRepository = {
findById: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
};
service = new UserService(mockRepository);
});
afterEach(() => {
jest.clearAllMocks();
});
// ... testes aqui
});
Casos happy path: testes para o fluxo principal, quando tudo funciona como esperado.
Testes de regressao: quando um bug e reportado, a IA pode gerar rapidamente um teste que reproduz o problema.
Cobertura de branches: a IA identifica branches nao testados e sugere casos de teste.
A IA falha em:
Testes de comportamento vs. implementacao: ela tende a testar COMO o codigo funciona, nao O QUE ele deveria fazer.
Edge cases de negocio: ela nao conhece as regras de negocio implicitas que voce nunca escreveu.
Testes de integracao reais: ela gera mocks demais, testando integracao falsa.
Validacao de requisitos: ela nao sabe se a implementacao esta correta, so se ela e consistente consigo mesma.
Para decidir quando confiar em testes gerados por IA, use o framework TRUST:
T - Testability (Testabilidade) O teste pode ser falsificado? Se nao existe input que faria o teste falhar, ele e inutil.
// Teste nao-falsificavel (ruim)
it('should return something', () => {
const result = doSomething();
expect(result).toBeDefined(); // Sempre passa se nao throw
});
// Teste falsificavel (bom)
it('should return user name', () => {
const result = getUser(1);
expect(result.name).toBe('Alice'); // Falha se nome for diferente
});
R - Relevance (Relevancia) O teste verifica um comportamento que importa para o usuario ou para o negocio?
// Irrelevante (testa detalhes internos)
it('should call validateInput before processing', () => {
const spy = jest.spyOn(service, 'validateInput');
service.process(data);
expect(spy).toHaveBeenCalled(); // Quem se importa?
});
// Relevante (testa comportamento observavel)
it('should reject invalid input with descriptive error', () => {
const result = service.process(invalidData);
expect(result.error).toBe('Email format is invalid');
});
U - Uniqueness (Unicidade) O teste verifica algo que nenhum outro teste verifica?
// Duplicado (coberto por outro teste)
it('should add item to cart', () => {
cart.add(item);
expect(cart.items).toContain(item);
});
it('should include item after adding', () => { // Mesmo teste!
cart.add(item);
expect(cart.items).toHaveLength(1);
});
S - Specificity (Especificidade) O teste falha por uma unica razao clara?
// Inespecifico (falha por multiplas razoes)
it('should process order correctly', () => {
const order = createOrder();
const result = processOrder(order);
expect(result.status).toBe('completed');
expect(result.total).toBe(100);
expect(result.items).toHaveLength(3);
expect(result.shipping).toBeDefined();
});
// Especifico (uma verificacao por teste)
it('should calculate total as sum of item prices', () => {
const order = createOrder([{ price: 30 }, { price: 70 }]);
expect(processOrder(order).total).toBe(100);
});
T - Traceability (Rastreabilidade) O teste pode ser rastreado a um requisito, historia de usuario ou bug report?
// Sem rastreabilidade
it('should handle edge case', () => { ... });
// Com rastreabilidade
it('should apply 10% discount for orders over R$100 (STORY-123)', () => { ... });
// Ou usando tags/metadata
it.todo('BUG-456: should not allow negative quantities');
Antes de aceitar um teste gerado por IA, verifique:
## Checklist TRUST para Testes Gerados por IA
### Testabilidade
- [ ] Existe um input que faria este teste falhar?
- [ ] O teste verifica um valor especifico (nao apenas "defined" ou "truthy")?
### Relevancia
- [ ] O teste verifica comportamento observavel pelo usuario?
- [ ] Se este teste falhar, indicaria um bug real?
### Unicidade
- [ ] Este teste cobre algo que nenhum outro teste cobre?
- [ ] Remover este teste reduziria a confianca na suite?
### Especificidade
- [ ] O teste verifica uma unica coisa?
- [ ] Se falhar, a mensagem de erro indica claramente o problema?
### Rastreabilidade
- [ ] O teste pode ser relacionado a um requisito ou bug?
- [ ] O nome do teste descreve o comportamento esperado?
### Red Flag Check
- [ ] O teste NAO espelha a implementacao (nao e tautologico)?
- [ ] O teste NAO usa mocks excessivos que escondem bugs de integracao?
Testes visuais tradicionais comparam screenshots pixel a pixel. Qualquer mudanca — intencional ou nao — falha o teste. Isso gera ruido: fontes renderizando diferente, anti-aliasing variando, animacoes em frames diferentes.
Ferramentas modernas usam IA para comparacao semantica: "a UI mudou de forma significativa para o usuario?"
// Configuracao Playwright com visual comparisons
import { test, expect } from '@playwright/test';
test('product page visual regression', async ({ page }) => {
await page.goto('/products/123');
// Espera elementos criticos carregarem
await page.waitForSelector('[data-testid="product-image"]');
await page.waitForLoadState('networkidle');
// Screenshot com threshold de tolerancia
await expect(page).toHaveScreenshot('product-page.png', {
maxDiffPixelRatio: 0.01, // Tolera 1% de diferenca
animations: 'disabled', // Desabilita animacoes para consistencia
mask: [
page.locator('[data-testid="timestamp"]'), // Mascara conteudo dinamico
page.locator('[data-testid="user-avatar"]'),
],
});
});
Ferramentas como Percy, Applitools e Chromatic usam IA para:
// Exemplo com Applitools Eyes
import { Eyes, Target } from '@applitools/eyes-playwright';
test('checkout flow visual test', async ({ page }) => {
const eyes = new Eyes();
await eyes.open(page, 'E-commerce App', 'Checkout Flow');
await page.goto('/cart');
await eyes.check('Cart Page', Target.window().fully());
await page.click('[data-testid="checkout-button"]');
await eyes.check('Shipping Form', Target.window().fully());
await page.fill('[name="address"]', '123 Test St');
await eyes.check('Shipping Form Filled', Target.region('[data-testid="shipping-form"]'));
await eyes.close();
});
Visual regression testing em CI precisa balancear cobertura com velocidade:
# Estrategia em camadas para visual testing
name: Visual Tests
on:
pull_request:
paths:
- 'src/components/**'
- 'src/pages/**'
- 'src/styles/**'
jobs:
visual-critical:
# Sempre roda: paginas criticas
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --grep @visual-critical
visual-full:
# So roda se arquivos de UI mudaram
if: contains(github.event.pull_request.labels.*.name, 'ui-changes')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test --grep @visual
Com grandes poderes vem grandes oportunidades de fazer besteira. Estes sao os anti-patterns mais comuns quando times adotam IA para testes.
Sintoma: mutation score baixo apesar de cobertura alta.
Causa: time usa IA para "aumentar cobertura" sem verificar qualidade dos testes.
Exemplo:
// IA gera para aumentar cobertura:
it('should cover the else branch', () => {
const result = myFunction(false);
expect(result).toBeDefined(); // Nao verifica nada util
});
Solucao: mutation score como metrica primaria, cobertura como secundaria.
Sintoma: testes unitarios passam, integracao falha em producao.
Causa: IA gera mocks para toda dependencia, testando codigo isolado que nunca roda isolado.
Exemplo:
// IA gera mocks excessivos:
const mockDb = { query: jest.fn().mockResolvedValue([]) };
const mockCache = { get: jest.fn().mockResolvedValue(null) };
const mockLogger = { info: jest.fn() };
const mockMetrics = { increment: jest.fn() };
// ... 10 mocks depois, o que estamos testando mesmo?
Solucao: testes de integracao reais com banco de dados de teste, containers, etc.
Sintoma: testes quebram constantemente quando codigo muda, mesmo sem bugs.
Causa: testes acoplados a detalhes de implementacao, nao a comportamento.
Exemplo:
// Teste acoplado a implementacao:
it('should call validateEmail then checkDomain', () => {
const spy1 = jest.spyOn(service, 'validateEmail');
const spy2 = jest.spyOn(service, 'checkDomain');
service.processEmail('test@example.com');
expect(spy1).toHaveBeenCalledBefore(spy2); // Quem se importa com a ordem?
});
Solucao: testar outputs, nao internals. Refatoracao nao deveria quebrar testes.
Sintoma: IA gera testes, IA review os testes, humano so aprova.
Causa: preguica de revisar testes "porque a IA ja checou."
Exemplo:
// IA gera teste, IA diz "looks good":
it('should return true when valid', () => {
expect(isValid('test')).toBe(true); // Mas o que e "valid"? Qual a regra?
});
Solucao: humano valida contra especificacao/requisitos. IA nao conhece o "should."
Sintoma: centenas de snapshots que ninguem realmente le nas diferencas.
Causa: IA sugere snapshot testing para tudo, time aceita sem criterio.
Exemplo:
// Snapshot de objeto complexo demais:
it('should match snapshot', () => {
const result = complexBusinessLogic(input);
expect(result).toMatchSnapshot(); // 200 linhas de JSON, quem vai revisar?
});
Solucao: snapshots apenas para output estavel e revisavel. Assertions explicitas para logica.
Transformar sua estrategia de testes nao acontece em um sprint. Aqui esta um roadmap de 12 semanas para times que querem adotar as praticas deste artigo.
Objetivo: entender o estado atual.
Acoes:
Entregavel: relatorio de baseline com mutation score e lista de testes tautologicos.
Objetivo: adicionar camada de property-based testing.
Acoes:
Entregavel: suite de property tests para modulos criticos, integrada ao CI.
Objetivo: mutation testing como gate de qualidade.
Acoes:
Entregavel: mutation testing automatizado com alertas para score abaixo do threshold.
Objetivo: cobertura visual para fluxos criticos.
Acoes:
Entregavel: visual tests para fluxos criticos, integrados ao CI de PRs.
Objetivo: processo de revisao para testes gerados por IA.
Acoes:
Entregavel: processo documentado e time treinado.
Objetivo: validar impacto e ajustar.
Acoes:
Entregavel: retrospectiva documentada com metricas de melhoria e plano de continuidade.
A promessa da IA em testes e tentadora: cobertura instantanea, testes gerados automaticamente, suites verdes sem esforco. Mas velocidade sem validacao e apenas uma forma mais rapida de falhar.
A estrategia que apresentei neste artigo nao rejeita a IA — ela a coloca no lugar certo. IA e excelente para boilerplate, para happy paths, para a parte mecanica dos testes. Mas a parte que realmente importa — a validacao de que os testes testam algo real — continua sendo responsabilidade humana.
Property-based testing encontra os edge cases que nem voce nem a IA imaginaram. Mutation testing prova que seus testes detectam bugs, nao apenas existem. O framework TRUST garante que cada teste gerado por IA passa por validacao humana antes de entrar na suite.
A Nova Piramide de Testes para a era da IA nao e mais lenta ou mais cara que a abordagem tradicional. E mais honesta. Ela troca a metrica vaidade (cobertura) pela metrica real (mutation score). Troca velocidade de geracao por confianca de validacao.
Um time com 60% de cobertura e 85% de mutation score esta em situacao melhor do que um time com 95% de cobertura e 40% de mutation score. O primeiro sabe o que esta testando. O segundo esta operando no escuro, confiando em numeros que nao significam nada.
A suite verde mais perigosa e aquela onde ninguem sabe se os testes realmente funcionam. A suite mais valiosa e aquela onde cada teste passou pelo crivo de "isso detectaria um bug real?"
Construa confianca. Nao a gere.
A qualidade dos seus testes define a qualidade do seu software. Invista em validacao, nao em cobertura. A diferenca entre uma suite que protege e uma suite que engana e simplesmente a pergunta: "este teste falharia se houvesse um bug?" Se voce nao pode responder com certeza, o teste nao existe de verdade.