La sortida dels teus tests està cremant tokens: Domant reporters verbosos per a agents d'AI
Els test runners com Jest i Vitest venen amb reporters dissenyats per a humans mirant terminals. Cada fitxer té una línia:
PASS src/components/Button.test.tsxPASS src/components/Card.test.tsxPASS src/components/Dialog.test.tsxPASS src/components/Dropdown.test.tsxPASS src/utils/format.test.tsPASS src/utils/date.test.ts... (200 more lines)FAIL src/components/Nav.test.tsx● Nav > renders active stateExpected: "active"Received: "inactive"
Per a un desenvolupador a la seva terminal, veure passar el text verd tranquil·litza. Però els tests ara s'executen en dos altres contexts on aquesta sortida costa més del que ajuda:
- CI — ningú llegeix el log a menys que alguna cosa falli. Una build vermella t'obliga a fer scroll més enllà de centenars de línies "PASS" per trobar la fallada.
- Agents d'AI llegeixen cada línia d'aquesta sortida. Cada línia "PASS" consumeix tokens, omplint la finestra de context amb sortida de test exitosa en lloc del problema real.
Ens vam trobar amb això a Buffer. Una execució de test
de 215 suites va produir ~3.500 tokens de sortida, gairebé tots línies "PASS".
El nostre agent d'AI va gastar més tokens llegint resultats de test que
escrivint codi. Vam provar d'afegir --reporter=dot a les nostres instruccions
de CLAUDE.md, però l'agent no sempre l'utilitzava. El flag era un suggeriment;
necessitàvem una garantia.
Detecta l'Environment, Tria el Reporter
El fix: detecta l'environment a la teva configuració de test i canvia els reporters automàticament. No calen instruccions per a l'agent, ni flags per recordar.
Claude Code estableix
CLAUDECODE=1 a cada
shell que genera. Els proveïdors de CI — GitHub Actions, GitLab CI, CircleCI,
Travis CI i Jenkins — tots estableixen
CI=true.
La teva configuració llegeix aquestes variables i tria el reporter correcte —
determinista independentment de com l'agent invoqui la comanda de test.
Això és el que vam posar en producció a Buffer. CI i Claude Code tenen cadascun la seva pròpia configuració de reporter; el desenvolupament local manté el default.
Per a Jest, afegeix la lògica a jest.config.ts. El
summary reporter
imprimeix un recompte final més detalls complets per a qualsevol fallada, sense
sortida per fitxer. El summaryThreshold per defecte de Jest és 20, és a dir,
només imprimeix detalls de fallades quan més de 20 tests fallen. Configura'l a
0 perquè cada fallada s'imprimeixi completa. A CI, pots combinar-lo amb un
reporter personalitzat que recopili fallades per a comentaris de PR a GitHub:
const isCI = process.env.CI === "true";const isClaude = process.env.CLAUDECODE === "1";function getReporters() {if (isCI) {return [["summary", { summaryThreshold: 0 }], "jest-ci-reporter"];}if (isClaude) {return [["summary", { summaryThreshold: 0 }]];}return ["default"];}export default {reporters: getReporters(),// ... rest of your config};
Per a Vitest, afegeix-ho a vitest.config.ts. El
dot reporter comprimeix
cada fitxer a un sol caràcter — un punt si passa, una x si falla:
import { defineConfig } from "vitest/config";const isCI = process.env.CI === "true";const isClaude = process.env.CLAUDECODE === "1";function getReporters() {if (isCI) {return ["dot", "ci-reporter"];}if (isClaude) {return ["dot"];}return ["default"];}export default defineConfig({test: {reporters: getReporters(),// ... rest of your config},});
Ambdós frameworks també accepten flags de reporter a la línia de comandes
(--reporters per a Jest,
--reporter per a Vitest). Però
confiar que un agent d'AI passi el flag correcte és probabilístic — l'agent pot
oblidar-se'n o executar un script de test diferent que l'ometi. Les variables
d'environment ho fan determinista.
La matriu resultant:
| Environment | Jest Reporter | Vitest Reporter |
|---|---|---|
| Desenvolupament local | default | default |
| CI | summary + CI reporter | dot + CI reporter |
| Claude Code | summary | dot |
Què veu l'Agent
Abans (reporter per defecte, ~250 línies):
PASS src/components/Button.test.tsx (3 suites, 12 tests)PASS src/components/Card.test.tsx (2 suites, 8 tests)... (200+ more PASS lines)FAIL src/components/Nav.test.tsx● Nav > renders active stateexpect(received).toBe(expected)Expected: "active"Received: "inactive"Test Suites: 1 failed, 214 passed, 215 totalTests: 1 failed, 847 passed, 848 total
Després (reporter summary, ~10 línies):
FAIL src/components/Nav.test.tsx● Nav > renders active stateexpect(received).toBe(expected)Expected: "active"Received: "inactive"Test Suites: 1 failed, 214 passed, 215 totalTests: 1 failed, 847 passed, 848 total
Els mateixos detalls de fallada, 96% menys de sortida.
Quan tots els tests passen, la bretxa s'eixampla encara més. El reporter per defecte imprimeix cada nom de fitxer — 215 línies. El reporter summary n'imprimeix dues:
Test Suites: 215 passed, 215 totalTests: 848 passed, 848 total
Compromisos
Perds el feedback de progrés. El reporter summary es manté en silenci fins que la suite acaba. Per a suites de llarga durada, l'agent no veu res fins a la finalització. A la pràctica això no ha importat — els agents d'AI no necessiten reassegurança que el procés està funcionant.
Fer debug de fallades intermitents és més difícil. El cronometratge per
fitxer del reporter per defecte ajuda a identificar tests lents o inestables.
Utilitza el reporter verbose quan investiguis la inestabilitat.
El mateix Fix s'aplica a Linters i Logs de Build
Els reporters de test són una interface entre les teves eines i el que sigui que
llegeixi la sortida. Els linters i els type checkers tenen el mateix problema. A
tot arreu on una eina produeixi sortida verbosa que un agent d'AI consumeixi,
pots detectar l'environment i canviar a un format compacte. Comprova la teva
configuració de test — si process.env.CI està establert i encara utilitzes el
reporter per defecte, estàs pagant per sortida que ningú llegeix.