MÓDULO 5.2

🧪 Testes em 3 níveis

Vitest co-localizado, mock backend compartilhado, WDIO dual-platform e cargo + mock. Plus o coverage gate de ≥80% em linhas alteradas que bloqueia merge.

6
Tópicos
50
Minutos
Avançado
Nível
Prática
Tipo
1

🎯 Três níveis, três responsabilidades

Cada nível pega uma classe diferente de bug. Confiar só em E2E é caro e lento; confiar só em unit é cego para integração. Pirâmide tradicional, aplicada ao stack Rust + React + Tauri.

📊 A pirâmide aplicada

Unit (Vitest + cargo test) — base larga

Rápido (segundos), barato, alta granularidade. Pega bugs de lógica isolada.

RPC E2E (json_rpc_e2e.rs) — meio

Médio (dezenas de segundos). Prova o contrato entre Rust e TS pelo transporte real.

App E2E (WDIO) — topo estreito

Lento (minutos). Prova o fluxo completo no app empacotado, com webdriver.

💡 Regra de ouro

Teste comportamento visível ao usuário, não implementação. Se o refactor não muda comportamento, o teste não deve quebrar. Se quebrou, ou o teste estava errado, ou o refactor mudou comportamento — em ambos os casos, decisão de produto.

Velocidade
Unit > RPC > E2E
Foco
Comportamento
Sem flake
Sem tempo real
Sem rede
Mock determinístico
2

⚡ Vitest: rápido e co-localizado

Testes ficam ao lado do código, como *.test.ts / *.test.tsx. Pastas __tests__ separadas viram cemitério — co-localização força disciplina.

# Full suite
$ pnpm test

# Com coverage
$ pnpm test:coverage

# Debug runner (output bounded, log em target/debug-logs/)
$ pnpm debug unit                                # tudo
$ pnpm debug unit src/components/Foo.test.tsx    # um arquivo
$ pnpm debug unit -t "renders empty state"       # por nome
$ pnpm debug unit Foo -t "renders" --verbose     # streaming raw

# Ver logs salvos
$ pnpm debug logs last --tail 100

✓ Bom teste Vitest

  • Testa render + interação do usuário
  • Usa helpers de app/src/test/
  • Mocka coreRpcClient, não fetch
  • Roda em <100ms cada

✗ Teste frágil

  • Asserta estado interno do componente
  • Depende de setTimeout real
  • Faz request HTTP real
  • Cobre só happy path
Config
vitest.config.ts
Setup
test/setup.ts
Helpers
app/src/test/
Cobertura
lcov.info
3

🎭 Mock backend compartilhado

Um único mock backend serve unit, RPC E2E e WDIO. Isso elimina drift de comportamento entre níveis. Você muda o mock uma vez, todos os testes veem a nova realidade.

📦 Arquivos do mock

  • scripts/mock-api-core.mjs — comportamento (handlers, dados in-memory)
  • scripts/mock-api-server.mjs — server HTTP que expõe o core
  • app/test/e2e/mock-server.ts — wrapper TS pro WDIO
# Subir o mock manualmente (debug)
$ pnpm mock:api

# Admin endpoints (uso programático nos testes)
GET  /__admin/health     # health check
POST /__admin/reset      # volta ao estado inicial
POST /__admin/behavior   # injeta erro / latência / shape
GET  /__admin/requests   # lista requests recebidas

🔄 Padrão de uso em spec

Antes de cada teste, faça POST /__admin/reset e configure o cenário via /__admin/behavior. Depois assertee usando /__admin/requests pra verificar que a UI chamou o que devia.

Resultado: testes determinísticos, sem ordem dependente, sem flake por estado residual.

Determinismo
/__admin/reset
Falhas
/__admin/behavior
Asserção
/__admin/requests
Único mock
Sem drift
4

🌐 WDIO em dois sistemas

E2E roda contra o app empacotado, não contra dev server. CI usa Linux com tauri-driver; dev local em macOS usa Appium Mac2 sobre o .app. Os dois caminhos têm que funcionar antes do PR.

🐧 Linux (CI)

  • tauri-driver + WebDriver :4444
  • • Sem janelas reais (headless)
  • • Usa Docker compose se necessário
  • docker compose -f e2e/docker-compose.yml run --rm e2e

🍎 macOS (local)

  • • Appium Mac2 (XCUITest :4723)
  • • Roda sobre o .app bundle
  • • Requer build prévio (pnpm test:e2e:build)
  • • Helpers nativos via clickNativeButton
# Build + roda spec único (cria workspace temp limpo)
$ pnpm test:e2e:build
$ bash app/scripts/e2e-run-spec.sh test/e2e/specs/smoke.spec.ts smoke

# Suite full de flows
$ pnpm test:e2e:all:flows

# Debug runner
$ pnpm debug e2e test/e2e/specs/cron-jobs-flow.spec.ts cron-jobs --verbose

⚠️ Cuidado

  • Nunca use seletores raw XCUIElementType* — fragiliza demais. Use element-helpers.ts.
  • • Não asserte presença de elemento DOM — asserte outcome (texto na tela, request no mock).
  • • Cada spec recebe workspace temp limpo via OPENHUMAN_WORKSPACE; não vaze estado entre specs.
Specs
app/test/e2e/specs/
Helpers
element-helpers.ts
Config
wdio.conf.ts
Workspace
Temp por spec
5

🦀 cargo + mock: provar o contrato

tests/json_rpc_e2e.rs é onde o contrato RPC vive. Rodando contra o mock backend, você prova que a serialização, auth e shape do payload funcionam ponta a ponta — sem precisar de UI.

# Suite Rust completa (sobe o mock automaticamente)
$ pnpm test:rust

# Um teste específico
$ bash scripts/test-rust-with-mock.sh --test json_rpc_e2e

# Filtro por nome
$ bash scripts/test-rust-with-mock.sh --test json_rpc_e2e -- cron::create

# Debug runner
$ pnpm debug rust              # tudo
$ pnpm debug rust json_rpc_e2e # filtrado

🧰 O que testar aqui

  • • Cada método RPC novo: happy path + erros de validação
  • • Auth: chamada sem bearer → 401
  • • Shape: response casa com o que a UI vai parsear
  • • Lifecycle: create → list → update → delete numa única sequência
Suite
tests/json_rpc_e2e.rs
Wrapper
test-rust-with-mock
Auth
Bearer token
Lifecycle
CRUD em sequência
6

📊 Coverage gate ≥80%

Não é cobertura global — é cobertura nas linhas que você acabou de mudar. diff-cover roda no CI sobre o LCOV mergeado de Vitest + cargo-llvm-cov (core + Tauri shell). Abaixo de 80%? PR não merge.

⚙️ Como funciona

  1. 1. CI roda pnpm test:coverage → gera app/coverage/lcov.info
  2. 2. CI roda cargo-llvm-cov sobre core + Tauri → gera LCOVs adicionais
  3. 3. Workflow merge dos LCOVs
  4. 4. diff-cover compara contra o diff do PR
  5. 5. Falha se <80% das linhas alteradas têm cobertura

✓ Estratégia que passa

  • Testar happy path e edge cases
  • Cobrir match/if com branches
  • Testar erros (RpcOutcome::Err)
  • Rodar coverage local antes de pushar

✗ Como falhar

  • Adicionar lógica nova sem teste
  • Testar só render() e ignorar interações
  • Marcar todo else como "raro"
  • Esperar CI mostrar e só depois adicionar testes

💡 Dica prática

Antes de pushar, rode pnpm test:coverage e olhe o lcov-report/index.html. Você vê em vermelho exatamente quais linhas precisam de teste. Resolva ali, evite o vai-e-vem com o CI.

Gate
≥80% diff lines
Tool
diff-cover
Rust
cargo-llvm-cov
Workflow
coverage.yml

🎓 Resumo do Módulo

Três níveis — unit rápido, RPC médio, E2E lento mas completo
Vitest co-localizado — testes ao lado do código, helpers compartilhados
Mock backend único — admin endpoints (reset/behavior/requests)
WDIO dual-platform — tauri-driver no Linux, Appium Mac2 no macOS
cargo + mock — json_rpc_e2e prova contrato pelo transporte
Coverage gate ≥80% — diff-cover em linhas alteradas, bloqueia merge

Próximo Módulo:

5.3 — Git, PRs e CI (fork model, branch, hooks, i18n parity)