Tag: TDD

  • Vitest en Angular 22: por qué Karma ya no es el default

    Vitest en Angular 22: por qué Karma ya no es el default

    Son las 11 de la noche. Hay un commit pendiente de mergear y el pipeline de CI acaba de arrancar.

    Primero levanta el contenedor. Después Chrome headless. Karma detecta los specs, los compila y — casi dos minutos después de tu push — arranca la primera suite.

    Multiplica esos dos minutos por cada PR del día, por cada rebase, por ese "se me olvidó un punto y coma" que te obliga a repetir el ciclo entero.

    No es una exageración. Es el ritual diario de cualquier equipo Angular con Karma en producción. Por eso Vitest en Angular 22 dejó de ser una curiosidad de nicho: es ya el camino que recomienda el propio equipo de Angular.


    Por qué Karma se queda atrás

    Karma no es lento porque esté mal hecho. Es lento porque hace algo que en 2026 ya no tiene sentido: lanzar un navegador real — Chrome, o el que hayas configurado — para ejecutar cada suite de tests.

    Arrancar un navegador tiene un coste. Inicializar el motor de renderizado, cargar las extensiones de test, compilar el bundle con la configuración heredada de karma.conf.js… todo eso pasa antes de que se ejecute el primer expect().

    Y luego está la ejecución. Karma corre las suites de forma secuencial por defecto. Si tienes 40 archivos de spec, esperas a que terminen uno detrás de otro.

    Yo he trabajado en proyectos donde levantar el entorno de Karma tardaba varios minutos, antes de correr un solo test útil. Multiplica eso por cada push a un pipeline que corre veinte veces al día y tienes un cuello de botella silencioso que nadie cuestiona porque "siempre ha sido así".

    Vitest cambia la premisa completa. En lugar de un navegador real, corre en un proceso de Node.js y simula el DOM con una librería de emulación — arranca en milisegundos, no en segundos. Y ejecuta los archivos de test en paralelo por defecto, no de forma secuencial.

    No hace falta inventar un benchmark con un múltiplo llamativo para explicar esto. La diferencia cualitativa ya es suficiente: uno lanza un navegador, el otro no.


    Vitest nativo en Angular 22: lo que es default y lo que no

    Desde Angular 21, Vitest es el framework de testing por defecto para proyectos nuevos creados con ng new. Angular 22 mantiene ese default. Aquí hay que ser preciso, porque el estado real tiene matices que se pierden en los titulares.

    Si generas un proyecto hoy con el CLI — tal y como lo hacemos desde cero en el curso de Angular Moderno —, Vitest ya viene configurado. No instalas nada, no tocas angular.json.

    Karma, por otro lado, sigue soportado oficialmente. No ha sido eliminado ni deprecado. Sigue siendo una opción válida y documentada si tienes un proyecto existente y decides quedarte con él.

    Lo que sí está marcado como experimental es otra cosa distinta: migrar un proyecto existente de Karma a Vitest. La documentación oficial de Angular lo dice sin rodeos: "Migrating an existing project to Vitest is considered experimental".

    Esa distinción importa. Vitest de fábrica en un proyecto nuevo es el camino estándar y recomendado. El proceso de migración de un proyecto legacy con Karma es lo que todavía se etiqueta como experimental. No son lo mismo, y confundirlos te hace tomar decisiones equivocadas sobre cuándo migrar.

    El builder detrás de todo esto se llama @angular/build:unit-test, y se configura en el target test de tu angular.json:

    {
      "projects": {
        "mi-proyecto": {
          "architect": {
            "test": {
              "builder": "@angular/build:unit-test"
            }
          }
        }
      }
    }
    

    Requiere el sistema de compilación application, que ya es el default en cualquier proyecto nuevo. Sus valores por defecto son "tsConfig": "tsconfig.spec.json" y "buildTarget": "::development" — no necesitas escribirlos a mano salvo que quieras cambiarlos.

    ¿Y el DOM? Vitest corre tus tests en un entorno Node.js, no en un navegador. Para simular document, window y el resto de la API del navegador usa una librería de emulación. El Angular CLI detecta automáticamente happy-dom si lo tienes instalado; si no, cae a jsdom como fallback.


    Cómo migrar un proyecto existente

    Si tu proyecto ya existe y corre sobre Karma, migrar no es instantáneo, pero tampoco es una reescritura. Son cinco pasos:

    1. Instala las dependencias: npm install --save-dev vitest jsdom
    2. Cambia el builder del target test en angular.json a @angular/build:unit-test
    3. Revisa tu karma.conf.js en busca de configuraciones custom y trasládalas a un vitest.config.ts
    4. Elimina karma.conf.js y src/test.ts, y desinstala los paquetes de Karma (karma, karma-chrome-launcher, karma-coverage, karma-jasmine, etc.)
    5. Opcional: si necesitas correr tests en un navegador real (modo browser), instala @vitest/browser-playwright y añade "browsers": ["chromium"] en la configuración

    Ahora el gotcha que rompe configuraciones cuando nadie lo espera.

    Con el builder viejo de Karma, podías meter tus opciones de build — polyfills, assets, estilos — directamente dentro del target test. Era cómodo, y casi nadie se paraba a pensar si estaba bien hecho.

    El builder nuevo, @angular/build:unit-test, no soporta eso. Si las opciones de build que necesitas para tus tests son distintas de las de tu configuración normal de desarrollo, tienes que sacarlas de ahí y crear una configuración de build dedicada — normalmente un target development separado que el builder de test referencia.

    Si tu proyecto tenía cualquier personalización de polyfills o assets dentro del target test, este es exactamente el punto donde la migración "automática" deja de serlo.


    El schematic que automatiza parte del trabajo

    Angular no te deja solo con los cinco pasos manuales. Existe un schematic que hace la parte mecánica de convertir sintaxis Jasmine a Vitest:

    ng generate @schematics/angular:refactor-jasmine-vitest --project mi-proyecto --add-imports
    

    Convierte automáticamente patrones como fit/fdescribe a it.only/describe.only, spyOn a vi.spyOn, jasmine.any a expect.any, y otras conversiones de sintaxis equivalentes.

    Opciones útiles: --project <nombre> para apuntar a un proyecto específico del workspace, --include <path> para limitar el alcance, --add-imports para que añada los imports explícitos de Vitest que necesites, y --browser-mode si estás migrando hacia modo browser.

    Ahora la parte honesta, porque prometerte una migración 100% automática sería mentirte.

    El schematic no instala dependencias — eso lo haces tú a mano. No migra polyfills ni assets — ese es el gotcha del punto anterior, y sigue siendo tu responsabilidad. Y en escenarios de spies complejos — mocks anidados, spies sobre spies, configuraciones de retorno encadenadas — hace su mejor esfuerzo, pero necesitas revisar el resultado a mano.

    Trátalo como un primer pase que te ahorra la mayor parte del trabajo mecánico, no como un botón de "migrar y olvidar".

    Si además ya usas IA para generar o revisar tus tests — algo que cubrimos en testing en Angular con IA —, dale el resultado del schematic a tu agente y pídele que revise específicamente los spies antes de dar la migración por terminada.


    Mapa de equivalencias: de Jasmine/Jest a Vitest

    Necesidad Jasmine/Jest Vitest
    Función simulada jest.fn() / jasmine.createSpy vi.fn()
    Espiar método jest.spyOn() vi.spyOn()
    Mockear módulo jest.mock() vi.mock()
    Import real en mock parcial jest.requireActual() vi.importActual()
    Timers falsos jest.useFakeTimers() vi.useFakeTimers()
    Restaurar mocks jest.clearAllMocks() vi.clearAllMocks()
    Matcher jasmine.any jasmine.any(Type) expect.any(Type)

    Fíjate en el patrón: casi todo lo que cambia empieza con jest. o jasmine. y pasa a vi.. Es el mocking y el motor de ejecución lo que cambia, no la forma de pensar tus tests.

    Los matchers de aserciones — toBe, toEqual, toContain, toThrow, resolves, rejects — funcionan prácticamente igual en Vitest. Si ya sabes escribir un expect() en Jasmine o Jest, sabes escribir uno en Vitest. La curva de aprendizaje no está en las aserciones, está en el mocking.

    Esto es justo lo que no cambia con el motor: los patrones de Testing Library (render, screen, userEvent) y la filosofía de testing por comportamiento en lugar de por implementación.

    Eso es exactamente lo que cubrimos en el curso de Testing en Angular con Jest y Testing Library: sea cual sea el motor de tu proyecto — Jest hoy, Vitest mañana —, cómo piensas un test de comportamiento no cambia.


    Testing zoneless con Vitest

    Angular 22 empuja fuerte hacia zoneless. Y eso cambia también cómo escribes tus tests.

    Con Zone.js, después de simular una interacción — un click, un input — a veces tenías que llamar fixture.detectChanges() manualmente para forzar que Angular actualizara la vista antes de tu expect().

    En modo zoneless no hay Zone.js escuchando cada tarea async para disparar la detección de cambios. En su lugar, usas await fixture.whenStable() para esperar a que el ciclo de detección de cambios asíncrono termine:

    it('actualiza el contador al hacer click', async () => {
      const fixture = TestBed.createComponent(ContadorComponent);
      fixture.nativeElement.querySelector('button').click();
    
      await fixture.whenStable();
    
      expect(fixture.nativeElement.textContent).toContain('1');
    });
    

    Es un cambio pequeño en la sintaxis pero grande en la intención: pasas de forzar la detección de cambios a esperar a que el propio sistema te diga que está estable. Es la misma filosofía que estamos viendo en otras piezas de v22, como Signal Forms — otra API que va madurando y sobre la que conviene ser precisos respecto a qué está ya estable y qué sigue en evolución.


    Karma vs Vitest en Angular 22, cara a cara

    Karma Vitest
    Arranque de suite Lanza un navegador real (Chrome u otro) Corre en Node.js, simula el DOM con happy-dom o jsdom
    Ejecución Secuencial por defecto Paralela por defecto
    Configuración karma.conf.js, heredada de webpack vitest.config.ts, integrada con el builder de Angular
    Estado en Angular 22 Soportado oficialmente, sigue siendo válido Default para proyectos nuevos; migrar proyectos existentes es experimental

    La tesis

    Cambiar de Karma a Vitest no es "un test runner más rápido". Es Angular alineando su tooling de testing con el ecosistema Vite y ESM que ya domina el resto del frontend — y quitándose de encima una dependencia que llevaba años siendo el cuello de botella silencioso de cualquier pipeline: un navegador real corriendo en CI.

    Si estás empezando un proyecto hoy, no tienes nada que decidir — Vitest ya viene puesto. Si tienes un proyecto existente con Karma, tienes una decisión real que tomar, y ahora sabes exactamente qué parte de esa migración es estándar y cuál sigue siendo experimental.

    Repasamos el resto de las novedades de v22 — de las que Vitest es solo una pieza — en el post de novedades de Angular v22. Y si quieres ver cómo aplicamos estos patrones en proyectos reales de producción, en Dominicode Labs es donde compartimos ese trabajo con la comunidad.


    Preguntas frecuentes sobre Vitest en Angular 22

    ¿Vitest reemplaza completamente a Karma en Angular 22?

    Reemplaza a Karma como default para proyectos nuevos, pero no lo elimina. Karma sigue soportado oficialmente y sigue siendo una opción documentada y válida si tienes un proyecto existente que prefieres no migrar todavía.

    ¿Necesito instalar plugins de terceros como Analog para usar Vitest en Angular 22?

    No. El soporte de Vitest está integrado directamente en el Angular CLI a través del builder @angular/build:unit-test. No necesitas ningún plugin de terceros para el flujo estándar — solo instalar vitest y jsdom (o happy-dom) como dependencias de desarrollo.

    ¿Cómo migro mis tests de Jasmine a Vitest automáticamente?

    Con el schematic ng generate @schematics/angular:refactor-jasmine-vitest, que convierte automáticamente la sintaxis de spies, matchers y bloques fit/fdescribe. No es una migración 100% automática: no instala dependencias, no migra polyfills ni assets, y los spies complejos necesitan revisión manual.

    ¿Qué le pasa a mis configuraciones de build al migrar de Karma a Vitest?

    Si tu configuración de build para tests (polyfills, assets, estilos) era distinta de tu configuración normal de desarrollo, no puedes moverla dentro del target test como hacías con Karma. El nuevo builder no lo soporta — tienes que crear una configuración de build dedicada, por ejemplo un target development separado.

    ¿Vitest funciona con testing zoneless en Angular 22?

    Sí, y de hecho es donde más se nota el cambio de paradigma: en lugar de llamar fixture.detectChanges() manualmente tras una interacción, usas await fixture.whenStable() para esperar el ciclo de detección de cambios asíncrono.

    ¿Debería migrar mi proyecto existente a Vitest hoy mismo?

    Si tu suite de tests es grande y crítica para producción, pruébalo primero en una rama o en un proyecto secundario antes de tocar el repo principal — la documentación oficial etiqueta esta migración como experimental. Depende, en última instancia, de tu tolerancia al riesgo.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Adopta TDD para implementar pruebas efectivas con agentes de IA

    Adopta TDD para implementar pruebas efectivas con agentes de IA

    Testing en la era de los agentes — TDD + agents, qué tests escribir tú vs cuáles el agente, snapshot testing inteligente

    Tiempo estimado de lectura: 5 min

    Ideas clave:

    • Usa tests como especificación (Test-Driven Prompting) y pide al agente implementar hasta que el test pase.
    • Los humanos deben definir contratos y tests críticos; los agentes pueden generar unit tests, edge cases y boilerplate bajo supervisión.
    • Cambia snapshots textuales por evaluaciones semánticas (LLM-judge) para reducir fragilidad.
    • Implementa un pipeline two-speed con sandboxes efímeros y telemetría sobre prompts y evaluaciones.

    Tabla de contenidos

    Introducción

    Testing en la era de los agentes — TDD + agents, qué tests escribir tú vs cuáles el agente, snapshot testing inteligente debe aparecer en tu playbook desde hoy. Si vas a dejar que un agente (Claude Code, Aider, Cursor u otros) escriba código en tu repo, tienes que reordenar qué pruebas son autoritativas, cuáles automatizas y cómo evitas cobertura falsa.

    Aquí está la estrategia práctica y accionable: cómo usar TDD como especificación, qué pruebas deben ser humanas, cuáles delegar a la IA, y cómo evolucionar los snapshots desde diffs frágiles a evaluaciones semánticas.

    Resumen rápido (lectores con prisa)

    Test-Driven Prompting aplica TDD a agentes: escribe el test como especificación y pide al agente implementar hasta que pase. Usa humanos para contratos, integración crítica y regresiones reales; delega unit tests deterministas y generación de edge cases al agente. Reemplaza snapshots textuales por una evaluación semántica (LLM-judge) que decide si un cambio es cosmético o funcional.

    Test-Driven Prompting: TDD + agents, qué tests escribir tú vs cuáles el agente

    Test-Driven Prompting es TDD adaptado a agentes. Escribes el test primero —no como un ejercicio de documentación, sino como la especificación no ambigua— y pides al agente que implemente hasta que el test pase. El test se convierte en el prompt más estricto que existe.

    Beneficios inmediatos:

    • El agente no improvisa requisitos; el test define el contrato.
    • Evitas cambios colaterales porque la suite actúa como guardrail.
    • La revisión humana se traslada de “¿funciona?” a “¿es esto sostenible y seguro?”.

    Pero atención: si el agente escribe la implementación y el test, obtendrás tautologías. Por eso la división de responsabilidades es crítica.

    División práctica: qué escribe el humano y qué el agente

    Lo que debe escribir el humano (no delegues)

    • Tests de integración críticos: pagos, auth, sincronización entre servicios. Requieren contexto de negocio y pruebas contra fallos reales.
    • Flujos E2E y guiones de usuario (Playwright, Cypress): definen la experiencia que no puede ser inferida por el agente. Playwright / Cypress
    • Tests de regresión con historial real: errores de producción documentados como tests que no pueden ser reinterpretados.
    • Setup de entornos y fixtures confiables: seeds de DB, contratos de mock globales.

    Lo que el agente puede generar (con supervisión)

    • Tests unitarios para funciones puras: transformaciones deterministas, utilidades, algoritmos puros.
    • Generación de edge cases y fuzzing estructurado: inputs nulos, límites, arrays extremos.
    • Boilerplate de mocks simples y factories (bajo reglas estrictas definidas por humanos).

    Pauta de revisión

    Cualquier test con mocking complejo (p. ej. jest.mock con comportamiento dinámico) debe pasar revisión manual antes de merge. Los LLMs tienden a “alucinar” APIs de mocking o a asumir comportamiento de librerías.

    Snapshot testing inteligente: de fragilidad a semántica

    Cómo funciona

    Los snapshots textuales mueren rápidos en repos de ritmo alto. La alternativa es snapshot testing inteligente: comparar semánticamente en lugar de por texto.

    Cómo funciona:

    • Generas snapshot tradicional (DOM/JSON).
    • Si cambia, un módulo de evaluación semántica (LLM-as-a-Judge) recibe: snapshot antiguo, snapshot nuevo y una rúbrica.
    • El modelo decide si el cambio es cosmético (aprobable) o funcional (falla y requiere revisión).

    Aplicaciones

    • UI: detectar si un botón cambió de estilo (aprobable) vs desapareció el control de envío (fallo).
    • APIs: admitir la adición de campos no utilizados por clientes y bloquear cambios en campos requeridos.

    Herramientas/ideas

    Construir un servicio interno que use un modelo de evaluación (ej. GPT-4o / Claude avanzado) y registre justificaciones estructuradas para auditoría.

    Pipeline recomendado y consideraciones operativas

    1. Golden Dataset de tests

    Golden Dataset de tests: 20–50 casos representativos versionados en Git. Sirve como baseline para evaluar cambios de spec.

    2. Two-speed CI

    • Cada PR: ejecución determinista rápida (linters, unit tests generados por agente, AST checks).
    • Merge/main: evaluación semántica completa (LLM-judge, snapshots semánticos).

    3. Sandbox seguro para deterministas

    Sandbox seguro para deterministas: ejecuta tests en contenedores efímeros sin red ni credenciales. Usa Firecracker o gVisor para aislamiento si ejecutas código generado automáticamente.

    4. Métricas y guardrails

    • Pass rate determinista por PR.
    • Delta semántico medio por cambio de spec.
    • Flakiness score (casos inestables entre ejecuciones).
    • Cost per eval (tokens, tiempo).

    Integración de herramientas de observabilidad: Promptfoo para orquestación local de evals, LangSmith para tracing, Braintrust para gestión de datasets.

    Checklist mínimo antes de confiar en un agente

    • Tests críticos escritos por humanos y en el repo.
    • Golden Dataset versionado y ejecutable en CI.
    • Sandbox efímero con timeouts y sin acceso a prod.
    • Reglas claras de qué puede commitear el agente automáticamente.
    • LLM-judge configurado para snapshots y revisiones semánticas.
    • Telemetría: registro de prompts, respuestas, tokens y justificación del juez.

    Conclusión: el rol del Tech Lead

    Testing en la era de los agentes no elimina la responsabilidad humana; la traslada a la definición de contratos, gobernanza y métricas. El Tech Lead debe decidir qué pruebas son la fuente de verdad, cómo se auditan las decisiones del agente y cuándo intervenir manualmente. Si tratas los tests como especificaciones inmutables y habilitas snapshots semánticos, los agentes dejan de ser generadores de ruido y pasan a ser máquinas de productividad que se pueden gobernar.

    Dominicode Labs

    Para equipos que exploran evaluaciones semánticas y pipelines con agentes, Dominicode Labs ofrece recursos y patrones reproducibles para integrar LLM-judges y sandboxes en CI. Considera esta referencia como una continuación práctica de las ideas descritas arriba.

    FAQ

    Respuesta: Test-Driven Prompting aplica la práctica de escribir tests como especificaciones antes de implementar. El test actúa como el prompt más estricto para el agente y define el contrato que debe cumplirse.

    Respuesta: No delegues tests de integración críticos (pagos, auth), flujos E2E, tests de regresión con historial real y el setup de entornos/fixtures. Estos requieren contexto de negocio y control humano.

    Respuesta: Los snapshots semánticos usan un módulo de evaluación (LLM-judge) que recibe el snapshot antiguo, el nuevo y una rúbrica, y decide si el cambio es cosmético o funcional, registrando justificación para auditoría.

    Respuesta: Un Golden Dataset incluye 20–50 casos representativos, versionados en Git y ejecutables en CI. Sirve como baseline para validar cambios de especificación.

    Respuesta: Ejecuta código en contenedores efímeros sin red ni credenciales, aplica timeouts y usa tecnologías como Firecracker o gVisor para aislamiento.

    Respuesta: Sigue métricas como pass rate determinista por PR, delta semántico medio por cambio de spec, flakiness score y cost per eval (tokens, tiempo). Complementa con telemetría de prompts y decisiones del juez.