|
| 1 | +# Auto Test Generation with Claude API — Step by Step |
| 2 | + |
| 3 | +Guia completo para replicar o fluxo de geração automática de testes unitários |
| 4 | +usando Claude API + GitHub Actions em qualquer repositório Android/KMP. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Visão geral do fluxo |
| 9 | + |
| 10 | +``` |
| 11 | +PR aberto |
| 12 | + → CI passa (pr.yml) |
| 13 | + → Workflow dispara (generate-tests.yml) |
| 14 | + → Escaneia arquivos sem cobertura |
| 15 | + → Chama Claude API para cada arquivo |
| 16 | + → Gera arquivos *Test.kt |
| 17 | + → Cria branch + abre PR automático com os testes |
| 18 | +``` |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Fase 1 — Análise do módulo |
| 23 | + |
| 24 | +Antes de configurar qualquer coisa, entenda o que existe no módulo alvo. |
| 25 | + |
| 26 | +**O que fazer:** |
| 27 | +- Mapear todos os arquivos `.kt` de produção (`commonMain`, `androidMain`) |
| 28 | +- Verificar se já existem testes em `src/test/java/` |
| 29 | +- Identificar quais arquivos têm ou não cobertura |
| 30 | +- Anotar o padrão de pacote (ex: `com.github.codandotv.craftd.androidcore`) |
| 31 | + |
| 32 | +**Comando útil para listar arquivos sem teste:** |
| 33 | +```bash |
| 34 | +while IFS= read -r SRC; do |
| 35 | + TEST=$(echo "$SRC" \ |
| 36 | + | sed 's|src/commonMain/kotlin/|src/test/java/|' \ |
| 37 | + | sed 's|src/androidMain/kotlin/|src/test/java/|' \ |
| 38 | + | sed 's|\.kt$|Test.kt|') |
| 39 | + if [ ! -f "$TEST" ]; then |
| 40 | + echo "SEM TESTE: $SRC" |
| 41 | + fi |
| 42 | +done < <(find <seu-modulo>/src \ |
| 43 | + \( -path "*/commonMain/kotlin/*.kt" -o -path "*/androidMain/kotlin/*.kt" \) \ |
| 44 | + | grep -v "Test\.kt" | sort) |
| 45 | +``` |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +## Fase 2 — Criar o script Python |
| 50 | + |
| 51 | +Crie o arquivo `.github/scripts/generate_tests.py`. |
| 52 | + |
| 53 | +**Responsabilidades do script:** |
| 54 | +- Receber a lista de arquivos via `CHANGED_FILES` (env var) ou args |
| 55 | +- Para cada arquivo, verificar se o teste já existe — se sim, pular |
| 56 | +- Converter o path fonte → path de teste: |
| 57 | + - `src/commonMain/kotlin/Foo.kt` → `src/test/java/FooTest.kt` |
| 58 | + - `src/androidMain/kotlin/Bar.kt` → `src/test/java/BarTest.kt` |
| 59 | +- Chamar a Claude API com o conteúdo do arquivo |
| 60 | +- Escrever o arquivo `*Test.kt` gerado no path correto |
| 61 | +- Exportar outputs para o GitHub Actions (`generated_count`, `covered_names`) |
| 62 | + |
| 63 | +**Modelo recomendado:** `claude-haiku-4-5-20251001` |
| 64 | +> Muito mais barato que Opus (~10x) e suficiente para gerar testes de data classes, |
| 65 | +> extension functions, DiffUtil callbacks e enums. |
| 66 | +
|
| 67 | +**Custo estimado:** ~$0.09 para 17 arquivos com Haiku. |
| 68 | + |
| 69 | +**Prompt que funcionou bem:** |
| 70 | +``` |
| 71 | +Você é um expert em Kotlin/KMP. Gere testes JUnit4 + MockK para o arquivo abaixo. |
| 72 | +- Pacote: {package_name} |
| 73 | +- Classe de teste: {ClassName}Test |
| 74 | +- Nomenclatura: backtick notation `given X when Y then Z` |
| 75 | +- Data classes: construção, defaults, copy(), equals/hashCode |
| 76 | +- Extension functions com JsonElement: null, JSON válido, JSON inválido |
| 77 | +- DiffUtil: areItemsTheSame e areContentsTheSame (incluindo AbstractMap) |
| 78 | +- Enums: verificar todos os valores via enumValueOf |
| 79 | +- Retorne APENAS o arquivo .kt, sem markdown, sem explicação. |
| 80 | +``` |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## Fase 3 — Criar o workflow |
| 85 | + |
| 86 | +Crie o arquivo `.github/workflows/generate-tests.yml`. |
| 87 | + |
| 88 | +**Triggers necessários:** |
| 89 | +```yaml |
| 90 | +on: |
| 91 | + workflow_dispatch: # execução manual pelo Actions UI |
| 92 | + inputs: |
| 93 | + override_files: |
| 94 | + description: 'Opcional: arquivos .kt específicos. Vazio = escaneia tudo.' |
| 95 | + required: false |
| 96 | + default: '' |
| 97 | + |
| 98 | + workflow_run: # automático após o CI passar |
| 99 | + workflows: ["Nome exato do seu workflow de CI"] |
| 100 | + types: [completed] |
| 101 | +``` |
| 102 | +
|
| 103 | +> ⚠️ `workflow_run` só funciona se o arquivo do workflow estiver na branch **default (main)**. |
| 104 | +> Enquanto o PR não for mergeado, o trigger automático não dispara. |
| 105 | + |
| 106 | +**Permissões necessárias:** |
| 107 | +```yaml |
| 108 | +permissions: |
| 109 | + contents: write |
| 110 | + pull-requests: write |
| 111 | +``` |
| 112 | + |
| 113 | +**Condição do job:** |
| 114 | +```yaml |
| 115 | +if: | |
| 116 | + github.event_name == 'workflow_dispatch' || |
| 117 | + ( |
| 118 | + github.event.workflow_run.conclusion == 'success' && |
| 119 | + github.event.workflow_run.actor.login != 'github-actions[bot]' |
| 120 | + ) |
| 121 | +``` |
| 122 | +> O check do bot evita loop infinito quando o próprio workflow abre um PR. |
| 123 | + |
| 124 | +**Steps do job (em ordem):** |
| 125 | + |
| 126 | +| Step | O que faz | |
| 127 | +|------|-----------| |
| 128 | +| Resolve trigger context | Normaliza `head_sha` e `pr_number` para os dois gatilhos | |
| 129 | +| Checkout | Usa `GH_PAT` (não `GITHUB_TOKEN`) para permitir push | |
| 130 | +| Set up Python | Versão 3.11 | |
| 131 | +| Install dependencies | `pip install anthropic` | |
| 132 | +| Find uncovered files | `find` nos diretórios `commonMain` e `androidMain` | |
| 133 | +| Generate tests | Chama `generate_tests.py` com `CHANGED_FILES` | |
| 134 | +| Check generated files | Usa `find` (não `git status`) para contar `*Test.kt` | |
| 135 | +| Commit tests | `git add --force` + push para nova branch | |
| 136 | +| Open PR | Usa `GH_TOKEN: ${{ secrets.GH_PAT }}` | |
| 137 | + |
| 138 | +--- |
| 139 | + |
| 140 | +## Fase 4 — Secrets necessários no repositório |
| 141 | + |
| 142 | +Configure em: **Settings → Secrets and variables → Actions** |
| 143 | + |
| 144 | +| Secret | Como obter | Para que serve | |
| 145 | +|--------|-----------|----------------| |
| 146 | +| `ANTHROPIC_API_KEY` | console.anthropic.com → API Keys | Autenticar chamadas à Claude API | |
| 147 | +| `GH_PAT` | github.com → Settings → Developer settings → Personal access tokens (classic) → escopo `repo` | Permitir que o workflow abra PRs | |
| 148 | + |
| 149 | +> ⚠️ **Por que `GH_PAT` e não `GITHUB_TOKEN`?** |
| 150 | +> O `GITHUB_TOKEN` tem restrição de segurança que impede a criação de PRs |
| 151 | +> que poderiam disparar outros workflows. O PAT pessoal não tem essa limitação. |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## Fase 5 — Armadilhas encontradas (e como evitar) |
| 156 | + |
| 157 | +### ❌ `git status --short` não detecta os arquivos gerados |
| 158 | +**Causa:** O diretório `src/test/java/` não existia no repo. O git lista diretórios |
| 159 | +novos como um único item untracked, sem expandir os arquivos dentro. |
| 160 | + |
| 161 | +**Solução:** Usar `find` para contar e listar os arquivos gerados: |
| 162 | +```bash |
| 163 | +COUNT=$(find seu-modulo/src/test -name "*Test.kt" 2>/dev/null | wc -l | tr -d ' ') |
| 164 | +``` |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +### ❌ `git add` não staged os arquivos de teste |
| 169 | +**Causa:** Diretório novo não rastreado pelo git. |
| 170 | + |
| 171 | +**Solução:** Usar `git add --force`: |
| 172 | +```bash |
| 173 | +git add --force seu-modulo/src/test/ |
| 174 | +``` |
| 175 | + |
| 176 | +--- |
| 177 | + |
| 178 | +### ❌ `workflow_run` usa a versão do workflow da `main`, não do PR |
| 179 | +**Causa:** Por design do GitHub Actions — `workflow_run` sempre lê o arquivo |
| 180 | +do workflow da branch default. |
| 181 | + |
| 182 | +**Solução:** Mergear o PR do workflow para a `main` antes de testar o fluxo automático. |
| 183 | +Para testar antes do merge, use `workflow_dispatch` manualmente. |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +### ❌ Actions não consegue criar PR (permission error) |
| 188 | +**Causa:** `GITHUB_TOKEN` não tem permissão para abrir PRs que disparam workflows. |
| 189 | + |
| 190 | +**Solução:** Criar um PAT pessoal com escopo `repo`, salvar como secret `GH_PAT` |
| 191 | +e usar `GH_TOKEN: ${{ secrets.GH_PAT }}` no step de criação de PR. |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +### ❌ Saldo insuficiente na Anthropic API |
| 196 | +**Causa:** A API key estava correta mas a conta não tinha créditos. |
| 197 | + |
| 198 | +**Solução:** Adicionar créditos em console.anthropic.com → Plans & Billing. |
| 199 | +Com Haiku, $5 cobrem ~50 execuções completas do módulo. |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +## Fase 6 — Como testar antes do merge |
| 204 | + |
| 205 | +**Opção 1 — Rodar o script localmente:** |
| 206 | +```bash |
| 207 | +source ~/.bash_profile # ou export ANTHROPIC_API_KEY=sk-ant-... |
| 208 | +
|
| 209 | +CHANGED_FILES=$( |
| 210 | + while IFS= read -r SRC; do |
| 211 | + TEST=$(echo "$SRC" \ |
| 212 | + | sed 's|src/commonMain/kotlin/|src/test/java/|' \ |
| 213 | + | sed 's|src/androidMain/kotlin/|src/test/java/|' \ |
| 214 | + | sed 's|\.kt$|Test.kt|') |
| 215 | + [ ! -f "$TEST" ] && echo "$SRC" |
| 216 | + done < <(find <seu-modulo>/src \ |
| 217 | + \( -path "*/commonMain/kotlin/*.kt" -o -path "*/androidMain/kotlin/*.kt" \) \ |
| 218 | + | grep -v "Test\.kt" | sort) |
| 219 | +) python3 .github/scripts/generate_tests.py |
| 220 | +``` |
| 221 | + |
| 222 | +**Opção 2 — Após mergear, usar `workflow_dispatch`:** |
| 223 | +``` |
| 224 | +GitHub → Actions → Auto Generate Cover+Test → Run workflow |
| 225 | +``` |
| 226 | +Deixe o campo vazio para escanear tudo, ou informe arquivos específicos. |
| 227 | +
|
| 228 | +--- |
| 229 | +
|
| 230 | +## Fase 7 — Verificar se funcionou |
| 231 | +
|
| 232 | +Após o workflow rodar, verifique: |
| 233 | +
|
| 234 | +1. **Step "Find uncovered Kotlin files"** — lista os arquivos detectados |
| 235 | +2. **Step "Generate unit tests with Claude API"** — cada arquivo deve mostrar `[OK] Written: ...` |
| 236 | +3. **Step "Check generated files"** — deve mostrar `Found N test file(s)` |
| 237 | +4. **Step "Open Pull Request"** — URL do PR gerado aparece no log |
| 238 | +5. **PR aberto automaticamente** com os testes em `src/test/java/...` |
| 239 | +
|
| 240 | +--- |
| 241 | +
|
| 242 | +## Estrutura de arquivos criados |
| 243 | +
|
| 244 | +``` |
| 245 | +.github/ |
| 246 | +├── scripts/ |
| 247 | +│ └── generate_tests.py # Script Python que chama a Claude API |
| 248 | +└── workflows/ |
| 249 | + ├── pr.yml # CI existente (build + test) |
| 250 | + └── generate-tests.yml # Workflow de geração automática de testes |
| 251 | + |
| 252 | +<seu-modulo>/ |
| 253 | +└── src/ |
| 254 | + └── test/ |
| 255 | + └── java/ |
| 256 | + └── com/... # Testes gerados espelhando o pacote do fonte |
| 257 | +``` |
| 258 | +
|
| 259 | +--- |
| 260 | +
|
| 261 | +## Checklist rápido para um novo repo |
| 262 | +
|
| 263 | +- [ ] Identificar o módulo alvo e mapear arquivos sem cobertura |
| 264 | +- [ ] Criar `.github/scripts/generate_tests.py` |
| 265 | +- [ ] Criar `.github/workflows/generate-tests.yml` apontando para o nome correto do CI |
| 266 | +- [ ] Adicionar secret `ANTHROPIC_API_KEY` (console.anthropic.com) |
| 267 | +- [ ] Adicionar secret `GH_PAT` (PAT com escopo `repo`) |
| 268 | +- [ ] Abrir PR com os arquivos do workflow e mergear para `main` |
| 269 | +- [ ] Abrir qualquer PR tocando o módulo alvo e acompanhar o Actions |
0 commit comments