Planos · agentic-dev-boilerplate
00 · tests que fijan comportamiento

Tests que fallan por el motivo correcto, antes del código.

/unit-testing diseña y escribe tests de una unidad: una función, una clase, un módulo. Cubre el ciclo red-green-refactor, cómo nombrar cada test, qué mockear y qué cobertura mirar. Corre sola, sobre el código que le señales, o a mitad de una historia, cuando /sprint-development encuentra un tramo que conviene hacer test-first.

para quien codea sirve con el runner que la app ya usa testea comportamiento, no implementación
Entrada

La unidad

Su interfaz pública es el contrato que los tests fijan.

→
Red

Un test que falla

El más chico posible, y falla por la aserción, no por un import roto.

→
Green

El mínimo código

Lo justo para pasar. La implementación real emerge en varios ciclos.

→
Refactor

Limpiar en verde

Con los tests como red de seguridad. Nunca con un test en rojo.

01 · cuándo usarla

Una unidad, sus tests, y nada más

Es una skill acotada a propósito: no lleva Jira, no despliega, no hace code review. Produce código testeado para un tramo y devuelve el control.

Úsala cuando

El pedido es sobre tests de una unidad

  • "Escribe unit tests para esta función o esta clase."
  • "Haz TDD de este tramo", "red-green-refactor".
  • "Qué mockeo aquí?" · "Cómo nombro este test?"
  • "Qué cobertura le pido a este módulo?"
  • Estás en el Stage 2 de /sprint-development y el tramo es lógica pura, ramas complejas o el reproductor de un bug.
No la uses para

Lo que pertenece a otra capa

  • Implementar una historia completa (plan, código, review, deploy): eso es /sprint-development, que puede llamar a esta skill por dentro.
  • Tests E2E o de integración: fuera de alcance. El navegador es /playwright-cli; el diseño de esos tests es de un flujo de QA aparte.
  • Correr la suite de regresión antes de un release, ni QA formal.
  • Deploy a producción.
Unit tests son trabajo de desarrollo, no de QA. Se escriben mientras se codea, no en una fase posterior. Por eso la skill no lee campos de Jira: un test unitario es una preocupación de quien escribe el código, no un artefacto del flujo.
02 · el mapa

Dos puertas de entrada y cuatro temas

Entras sola o desde una historia en curso. Lo que pides elige la referencia que la IA lee; cuando no está claro, arranca por la guía general.

03 · qué lee

Lo que lee antes de escribir un test

Es el orden canónico para una IA que arranca en frío. Corta antes cuando lo que sigue no aporta nada para el tramo.

La unidad bajo test

Primero su interfaz pública: es el contrato que los tests tienen que fijar.

Los tests que ya existen

Los *.test.ts o *.spec.ts de la misma carpeta. Se extienden, no se duplican.

Quién la llama

Buscar el símbolo dice qué colaboradores son externos (se mockean) y cuáles internos (se usan reales).

La configuración del runner

vitest.config.ts, jest.config.ts o equivalente: variables, archivos de setup, umbrales de cobertura, alias de rutas.

Helpers y fixtures vecinos

Se reusan las costuras del proyecto en lugar de inventar otras paralelas.

Requisitos

Un runner y su comando

  • Un runner configurado, el que nombra stack.test_runner en .agents/project.yaml.
  • El comando de tests declarado en package.json. Para TDD, que el runner tenga modo --watch.
  • El setup corre solo cuando stack.test_runner es null (sección de setup de references/unit-testing.md). Con cualquier otro valor se usa ese runner: nunca se instala un segundo ni se cambia por otro.
Contexto del proyecto

Stack y skills complementarias

  • El runner sale de stack.test_runner, el comando de stack.scripts.test y los tests viven bajo stack.app_root, todo en .agents/project.yaml.
  • Al entrar resuelve las skills de la categoría language desde .agents/skills/REGISTRY.md: las del repo o de la comunidad se cargan solas; una de tu usuario te la pregunta una vez.
04 · puerta 1

Sola: del código señalado a un reporte de cobertura

Sin /sprint-development de por medio, trabaja contra el código que le indiques y se arregla sola con lo que encuentre.

La IA hace

Identifica la unidad y decide si va test-first o después. Escribe los tests con la convención de nombres del proyecto y mockea solo las dependencias externas.

Corre la suite hasta verde y mide la cobertura de esa unidad.

Tú decides

Qué unidad, cuando el pedido es ambiguo: la IA te lo confirma antes de escribir.

El runner, solo si stack.test_runner es null: la IA te pregunta y la elección se guarda con bun run agents:setup --stack.

Sale

Archivos de test junto al código, con el mismo nombre más .test.ts.

Un reporte: archivos agregados, tests que pasan y el delta de cobertura.

Gate tests en verde y cobertura de la unidad verificada.
05 · puerta 2

Desde el Stage 2 de una historia: un tramo y de vuelta

En /sprint-development, el Stage 2 (implementación) marca el TDD como opcional. Cuando un tramo lo amerita, delega en esta skill y retoma cuando la unidad quedó en verde.

Qué tramo lo amerita

Una función de lógica pura.

Ramas complejas con casos borde: mejor fijarlos con tests que adivinarlos después.

Un bug: primero el test que lo reproduce y falla, después el arreglo.

Qué no hace

No mueve el ticket en Jira, no abre el PR, no hace review ni deploy.

Todo eso sigue siendo de /sprint-development. El límite entre las dos skills es informal y las dos lo conocen.

Devuelve

El tramo en verde y refactorizado.

El Stage 2 sigue con su verificación en paralelo: lint, tipos y unit tests, y vuelve a iterar si algo queda en rojo.

Si pides… Entra por
"Escribe unit tests para esta función, TDD" /unit-testing directo: un pedido de tests saltea al orquestador.
"Implementa esta historia: código, tests y PR" /sprint-development. Puede llamar a /unit-testing por dentro, pero la entrada es el orquestador.
06 · red-green-refactor

El ciclo corto es el que da el valor

Test antes del código, en ciclos de cinco a diez minutos. Si un ciclo pasa de media hora, el tramo es demasiado grande: se parte en pasos de comportamiento más chicos.

Red · un test que falla

El test más chico que captura un pedazo de comportamiento. Tiene que fallar por la aserción; uno que falla porque el archivo no compila no informa nada. Si pasa en la primera corrida, algo huele mal: se investiga antes de seguir.

Green · el mínimo código

Lo más simple que lo pase. Un valor fijo vale en esta etapa. Nada de código para tests que todavía no existen.

Refactor · solo en verde

Extraer, renombrar, deduplicar, con los tests vigilando. Refactorizar en rojo es perder la red de seguridad justo cuando más hace falta.

Tipo de código Test-first o después?
Lógica de negocio, funciones puras TDD, muy recomendado
Algoritmos, parsers, máquinas de estado, validaciones TDD
Corrección de un bug TDD: el test que reproduce el bug va primero
Código crítico en rendimiento TDD, con benchmarks junto a las aserciones
Componentes de UI presentacionales Después (o tests de componente, que a menudo se saltean)
Pegamento de integración (wrappers de DB, clientes de API) Después; lo cubren mejor los tests de integración
Scripts descartables, migraciones de una vez Se saltea

regla práctica: si tiene ramas, cuentas o parsing de strings, va con TDD; si casi solo orquesta otras cosas, el test va después o lo cubre un nivel más alto

Tests en cascadaEscribir todos los tests y después todo el código no es TDD: se pierde la retroalimentación de diseño de los ciclos chicos.
Verde de un golpeCincuenta líneas para pasar un test: el test no está guiando el diseño. Se achica el tramo.
No correr tras cada cambioRompe el ciclo. Para eso está el modo watch.
07 · nombres

Un nombre que explica la falla en una línea

El nombre de un test es lo que ves cuando falla, casi siempre en un log de CI y meses después. Si no se puede nombrar en pocas palabras, el test hace demasiado.

AAA · estructura del cuerpo

Arrange, Act, Assert

  • Preparar las entradas, invocar la unidad, verificar el resultado.
  • El nombre dice qué es verdad, en forma declarativa.
  • Corto y fácil de escanear; menos explicativo cuando el comportamiento es complejo.
Given-When-Then · estilo BDD

Precondición, acción, resultado

  • Se lee como una especificación.
  • Útil para onboarding o para quien no codea.
  • Verboso: en un test trivial resulta ceremonioso.
El híbrido que recomienda la skill: bloques describe cortos llevan el contexto (el "given"), y cada it dice el comportamiento. Contexto jerárquico, mensajes de falla que se explican solos, nada repetido.
describe('cart with items', () => {
  it('returns total including tax when item added', () => { ... });
  it('returns 0 when all items removed', () => { ... });
});

describe('cart with no items', () => {
  it('throws when attempting checkout', () => { ... });
});
Mal Bien
testGetUser returnsUserWhenIdExists
testError throwsValidationErrorOnEmptyEmail
testBoundary appliesDiscountAtExactly100Threshold
callsApiOnce un nombre de comportamiento, no de interacción con el mock

cuando la misma regla vale para muchas entradas, se parametriza y se nombra la regla, no cada caso

08 · qué mockear

Se mockea el borde del sistema, no lo que está adentro

Un test que mockea todos los colaboradores verifica el grafo de mocks, no el código. Las implementaciones reales van por defecto; el mock queda para lo que hace al test lento, frágil o no determinista.

Herramienta Para qué
Stub Reemplaza la implementación por un retorno fijo.
Spy Envuelve la implementación real y observa las llamadas sin cambiar el comportamiento.
Fake Una implementación que funciona pero simplificada: una base en memoria, un reloj falso.
Mock Expectativa programada que falla si no se la llama como se esperaba. Tiende a atar el test a la implementación: se prefieren spies y stubs.
Se mockea

Los bordes externos

  • HTTP, clientes de base de datos, filesystem.
  • El tiempo (timers falsos o un reloj inyectado) y el azar (semilla o generador inyectado).
  • Código que depende del entorno: process.env, módulos nativos.
No se mockea

Lo que estás probando

  • La unidad bajo test: sería testear el mock.
  • Funciones puras: son deterministas, se llaman y listo.
  • Lo que no es tuyo, cuando se puede evitar: se envuelve la librería en un adaptador propio y se mockea el adaptador.
Primero la inyección de dependencias. Si la función recibe lo que necesita como argumento, el test le pasa un fake y listo, sin parchear módulos. jest.mock o vi.mock quedan para cuando la costura es inevitable: efectos al cargar un módulo, un SDK de terceros.
// service.ts: la dependencia entra como argumento
export async function getUser(id: string, db = defaultDb) {
  return db.users.findOne({ id });
}

// service.test.ts: un fake, sin jest.mock
it('returns the user', async () => {
  const fakeDb = { users: { findOne: async () => ({ id: '1', name: 'A' }) } };
  expect(await getUser('1', fakeDb)).toEqual({ id: '1', name: 'A' });
});
Mocks que no se limpianEstado que se filtra entre tests y falsos positivos. Se limpian en beforeEach, y un spy se restaura al terminar.
Cinco dependencias mockeadasSi armar los mocks lleva más líneas que el test, la unidad no es una unidad: se testean las hojas, y el orquestador va a un test de integración o usa fakes.
Un mock async mal elegidomockResolvedValue contra mockReturnValue: el equivocado rompe en silencio.
09 · cobertura

Una alarma de humo, no una meta

Cobertura baja significa tests que faltan. Cobertura alta no significa tests buenos: un test que llama la función y no afirma nada también suma líneas. Lo útil es el delta de cada PR, no el número global.

Qué medir

Ramas antes que líneas

  • Branch coverage es la métrica más accionable: cada lado de cada if, &&, || y ternario.
  • Mucha cobertura de líneas con poca de ramas es la mitad de los condicionales sin probar.
  • Mutation testing (Stryker en JS/TS) inyecta errores chicos y mira si algún test lo nota. Lento: semanal o nocturno, en los módulos críticos.
En CI

Gates por delta, no un umbral fijo

  • Avisar si un PR baja la cobertura más de 2 puntos; bloquear si baja más de 5.
  • Fallar si un archivo de ruta crítica, declarado explícitamente, cae bajo su umbral.
  • Un umbral global único castiga el refactor que borra código testeado y premia el relleno.
Tipo de código Cobertura sugerida
Lógica de negocio, funciones puras 90-100%
Métodos de API pública 90% o más
Helpers internos 70-90%
Componentes de UI presentacionales 60-80% (el resto lo cubre E2E)
Pegamento, configuración, wiring 50% o más, o se saltea
Código generado, scripts descartables se excluye

son guías, no contratos: un helper al 70% que ejercita cada rama importante vale más que uno al 95% cuyo 5% faltante es el único camino de error

Se excluye el ruido para que el número refleje huecos reales: código generado (tipos, OpenAPI), copias de terceros, archivos *.d.ts, configuración, migraciones, stories y los propios tests. Si se excluye todo, la métrica no dice nada.
10 · rieles de seguridad

Lo que la skill nunca hace

Los antipatrones completos viven en SKILL.md y viajan en el registro compacto que reciben los subagentes (.agents/skills/REGISTRY.md).

No testea detalles de implementación: helpers privados, estado interno, cantidad de llamadas a métodos internos. Lo interno se refactoriza libre; los tests no deberían enterarse.
No se saltea el rojo. El test va antes del código y falla por el motivo correcto.
No usa aserciones débiles como toBeTruthy() o toBeDefined() cuando se espera un valor exacto.
No comparte estado mutable entre tests. Se resetea en beforeEach o se crea dentro del test; si no, aparecen fallas que dependen del orden.
No deja pasar un test flaky en verde. Se arregla la causa (tiempos, estado compartido, red) o se pone en cuarentena con un ticket.
No testea el framework: matchers, internos de una librería, mecánica del ORM. Se testea tu lógica.
No sigue sin la documentación oficial cuando la necesita: si no hay herramienta de documentación de librerías disponible, se detiene y te dice cuál falta, sin un sustituto silencioso.
11 · qué le dices a la IA

Invocarla en las tres terminales

Esta skill no tiene modos con nombre: lo que pides elige la referencia. Nombras la skill y describes el tramo, en español o en inglés.

Claude Code

/unit-testing TDD this function

La barra carga la skill; el resto de la frase elige el tema.

OpenCode

carga la skill unit-testing: qué mockear aquí

Sin barra: nombras la skill en la frase.

Codex

carga la skill unit-testing: write unit tests for this function

Mismo patrón que OpenCode, CLI o Desktop.

Si dices algo como… La IA lee
"TDD this function" · "red-green-refactor" · "test before code" tdd-workflow.md
"name this test" · "AAA" · "Given-When-Then" test-naming.md
"qué mockear" · "Vitest spy" · "DI for testing" mocking-patterns.md
"coverage target" · "mutation testing" test-coverage.md
principios generales · setup del runner unit-testing.md

las referencias viven en .agents/skills/unit-testing/references/ · ante la duda, la IA arranca por unit-testing.md, que apunta a las demás

12 · alrededor de esta skill

A quién le pasa la posta

Ida y vuelta · historias

/sprint-development

Llama a esta skill en el Stage 2 para un tramo TDD o el reproductor de un bug, y retoma cuando la unidad está en verde y refactorizada.

Antes · base técnica

/project-bootstrap

Monta el repo y deja los tests unitarios como tema aparte, para correr en cualquier momento después. Deck: Project bootstrap.

Fuera de alcance · navegador

/playwright-cli

Para lo end-to-end contra la app corriendo. El diseño de tests de integración y E2E pertenece a un flujo de QA aparte.

Después · git

/git-flow-master

Los tests viajan en el mismo commit que el comportamiento que verifican, no en un commit test: aparte.

Para leer más: .agents/skills/unit-testing/SKILL.md y su carpeta references/, una referencia por tema.