协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
tdd-guide【开发与工程】Skill de desenvolvimento orientado a testes para escrever testes unitários, gerar fixtures e mocks de teste, analisar lacunas de cobertura e guiar workflows red-green-refactor em Jest, Pytest, JUnit, Vitest e Mocha. Use quando o usuário pedir para escrever testes, melhorar cobertura de testes, praticar TDD, gerar mocks ou stubs, ou mencionar framewor…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: "tdd-guide" description: "Skill de desenvolvimento orientado a testes para escrever testes unitários, gerar fixtures e mocks de teste, analisar lacunas de cobertura e guiar workflows red-green-refactor em Jest, Pytest, JUnit, Vitest e Mocha. Use quando o usuário pedir para escrever testes, melhorar cobertura de testes, praticar TDD, gerar mocks ou stubs, ou mencionar frameworks de teste como Jest, pytest ou JUnit." agents:
Skill de desenvolvimento orientado a testes para gerar testes, analisar cobertura e guiar workflows red-green-refactor em Jest, Pytest, JUnit e Vitest.
test_generator.py com os requisitosnpm test -- --coverage)coverage_analyzer.py no relatório LCOV/JSON/XMLtdd_workflow.py --phase red para validartdd_workflow.py --phase green para validarFunção-fonte de entrada (math_utils.py):
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
Comando:
python scripts/test_generator.py --input math_utils.py --framework pytest
Saída de teste gerada (test_math_utils.py):
import pytest
from math_utils import divide
class TestDivide:
def test_divide_positive_numbers(self):
assert divide(10, 2) == 5.0
def test_divide_negative_numerator(self):
assert divide(-10, 2) == -5.0
def test_divide_float_result(self):
assert divide(1, 3) == pytest.approx(0.333, rel=1e-3)
def test_divide_by_zero_raises_value_error(self):
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(10, 0)
def test_divide_zero_numerator(self):
assert divide(0, 5) == 0.0
Comando:
python scripts/coverage_analyzer.py --report lcov.info --threshold 80
Exemplo de saída:
Relatório de Cobertura — Geral: 63% (threshold: 80%)
P0 — Lacunas críticas (caminhos de erro não cobertos):
auth/login.py:42-58 handle_expired_token() 0% coberto
payments/process.py:91-110 handle_payment_failure() 0% coberto
P1 — Lacunas de alto valor (branches de lógica central):
users/service.py:77 update_profile() — branch else 0% coberto
orders/cart.py:134 apply_discount() — guarda de qtd zero 0% coberto
P2 — Lacunas de baixo risco (funções utilitárias / auxiliares):
utils/formatting.py:12 format_currency() 0% coberto
Recomendado: Gere testes para itens P0 primeiro para atingir threshold de 80%.
| Ferramenta | Propósito | Uso |
|---|---|---|
test_generator.py | Gerar casos de teste a partir de código/requisitos | python scripts/test_generator.py --input source.py --framework pytest |
coverage_analyzer.py | Analisar e processar relatórios de cobertura | python scripts/coverage_analyzer.py --report lcov.info --threshold 80 |
tdd_workflow.py | Guiar ciclos red-green-refactor | python scripts/tdd_workflow.py --phase red --test test_auth.py |
fixture_generator.py | Gerar dados de teste e mocks | python scripts/fixture_generator.py --entity User --count 5 |
Scripts adicionais: framework_adapter.py (converter entre frameworks), metrics_calculator.py (métricas de qualidade), format_detector.py (detectar linguagem/framework), output_formatter.py (saída CLI/desktop/CI).
Para Geração de Teste:
Para Análise de Cobertura:
Para Workflow TDD:
TDD é mais eficaz quando orientado por uma spec escrita. O fluxo:
specs/<feature>.mdproject/
├── specs/
│ ├── user-auth.md # Spec da funcionalidade com critérios de aceitação
│ ├── payment-processing.md
│ └── notification-system.md
├── tests/
│ ├── test_user_auth.py # Testes derivados de specs/user-auth.md
│ ├── test_payments.py
│ └── test_notifications.py
└── src/
Cada critério de aceitação em uma spec mapeia para pelo menos um teste:
| Critério da Spec | Caso de Teste |
|---|---|
| "Usuário pode fazer login com credenciais válidas" | test_login_valid_credentials_returns_token |
| "Senha inválida retorna 401" | test_login_invalid_password_returns_401 |
| "Conta bloqueia após 5 tentativas falhas" | test_login_locks_after_five_failures |
Dica: Numere seus critérios de aceitação na spec. Referencie o número na docstring do teste para rastreabilidade (# AC-3: Conta bloqueia após 5 tentativas falhas).
Cross-reference: Veja
engineering/spec-driven-workflowpara a metodologia completa de spec, incluindo templates de spec e checklists de revisão.
// test/cart.test.ts
describe("Cart", () => {
describe("addItem", () => {
it("deve adicionar um novo item a um carrinho vazio", () => {
const cart = new Cart();
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
expect(cart.items).toHaveLength(1);
expect(cart.items[0].id).toBe("sku-1");
});
it("deve incrementar quantidade ao adicionar item existente", () => {
const cart = new Cart();
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 2 });
expect(cart.items).toHaveLength(1);
expect(cart.items[0].qty).toBe(3);
});
it("deve lançar quando quantidade é zero ou negativa", () => {
const cart = new Cart();
expect(() =>
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 0 })
).toThrow("Quantity must be positive");
});
});
});
# tests/conftest.py — fixtures compartilhadas
import pytest
from app.db import create_engine, Session
@pytest.fixture(scope="session")
def db_engine():
engine = create_engine("sqlite:///:memory:")
yield engine
engine.dispose()
@pytest.fixture
def db_session(db_engine):
session = Session(bind=db_engine)
yield session
session.rollback()
session.close()
# tests/test_pricing.py — parametrize para múltiplos casos
import pytest
from app.pricing import calculate_discount
@pytest.mark.parametrize("subtotal, expected_discount", [
(50.0, 0.0), # Abaixo do threshold — sem desconto
(100.0, 5.0), # Tier de 5%
(250.0, 25.0), # Tier de 10%
(500.0, 75.0), # Tier de 15%
])
def test_calculate_discount(subtotal, expected_discount):
assert calculate_discount(subtotal) == pytest.approx(expected_discount)
// cart_test.go
package cart
import "testing"
func TestApplyDiscount(t *testing.T) {
tests := []struct {
name string
subtotal float64
want float64
}{
{"sem desconto abaixo do threshold", 50.0, 0.0},
{"tier de 5 porcento", 100.0, 5.0},
{"tier de 10 porcento", 250.0, 25.0},
{"tier de 15 porcento", 500.0, 75.0},
{"subtotal zero", 0.0, 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ApplyDiscount(tt.subtotal)
if got != tt.want {
t.Errorf("ApplyDiscount(%v) = %v, want %v", tt.subtotal, got, tt.want)
}
})
}
}
Ao gerar testes autonomamente, siga estas regras para decidir quando parar e perguntar ao usuário:
Testes baseados em propriedades geram entradas aleatórias para verificar invariantes em vez de depender de exemplos escolhidos manualmente. Use quando o espaço de entrada é grande e o comportamento esperado pode ser descrito como uma propriedade.
from hypothesis import given, strategies as st
from app.serializers import serialize, deserialize
@given(st.text())
def test_roundtrip_serialization(data):
"""Serialização seguida de desserialização retorna o original."""
assert deserialize(serialize(data)) == data
@given(st.integers(), st.integers())
def test_addition_is_commutative(a, b):
assert a + b == b + a
import fc from "fast-check";
import { encode, decode } from "./codec";
test("roundtrip encode/decode", () => {
fc.assert(
fc.property(fc.string(), (input) => {
expect(decode(encode(input))).toBe(input);
})
);
});
| Use Baseado em Propriedades | Exemplo |
|---|---|
| Transformações de dados | Roundtrips de serialização/desserialização |
| Propriedades matemáticas | Comutatividade, associatividade, idempotência |
| Codificação/decodificação | Base64, codificação URL, compressão |
| Ordenação e filtragem | Saída está ordenada, comprimento preservado |
| Correção de parser | Entrada válida sempre parseia sem erro |
Testes de mutação modificam seu código de produção (criam "mutantes") e verificam se seus testes detectam as mudanças. Se um mutante sobreviver (testes ainda passam), seus testes têm uma lacuna que a cobertura sozinha não consegue revelar.
| Linguagem | Ferramenta | Comando |
|---|---|---|
| TypeScript/JavaScript | Stryker | npx stryker run |
| Python | mutmut | mutmut run --paths-to-mutate=src/ |
| Java | PIT | mvn org.pitest:pitest-maven:mutationCoverage |
< para <= expõem lacunas de off-by-oneRecomendação: Execute testes de mutação em caminhos críticos (auth, pagamentos, processamento de dados) mesmo que a cobertura geral seja alta. Mire 85%+ de score de mutação em módulos P0.
| Skill | Relação |
|---|---|
engineering/spec-driven-workflow | Pipeline spec → critérios de aceitação → extração de testes |
engineering-team/focused-fix | Fase 5 (Verificar) usa TDD para confirmar a correção com um teste de regressão |
engineering-team/senior-qa | Estratégia de QA mais ampla; TDD é uma camada na pirâmide de testes |
engineering-team/code-reviewer | Revisar testes gerados para qualidade de assertion e completude de cobertura |
engineering-team/senior-fullstack | Scaffolders de projeto incluem infraestrutura de teste compatível com workflows TDD |
| Escopo | Detalhes |
|---|---|
| Foco em testes unitários | Testes de integração e E2E requerem padrões diferentes |
| Análise estática | Não pode executar testes ou medir comportamento em runtime |
| Suporte a linguagens | Melhor para TypeScript, JavaScript, Python, Java |
| Formatos de relatório | Apenas LCOV, JSON, XML; outros formatos precisam de conversão |
| Testes gerados | Fornecem scaffolding; requerem revisão humana para lógica complexa |
Quando usar outras ferramentas: