<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bezael Pérez</title>
    <description>The latest articles on DEV Community by Bezael Pérez (@bezael).</description>
    <link>https://dev.to/bezael</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69894%2F8c8240fd-1dd9-4fbe-8277-26c618359d1b.jpg</url>
      <title>DEV Community: Bezael Pérez</title>
      <link>https://dev.to/bezael</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bezael"/>
    <language>en</language>
    <item>
      <title>SDD en proyectos brownfield: pros, contras y la estrategia que realmente funciona</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Tue, 26 May 2026 15:51:42 +0000</pubDate>
      <link>https://dev.to/bezael/sdd-en-proyectos-brownfield-pros-contras-y-la-estrategia-que-realmente-funciona-8i8</link>
      <guid>https://dev.to/bezael/sdd-en-proyectos-brownfield-pros-contras-y-la-estrategia-que-realmente-funciona-8i8</guid>
      <description>&lt;h2&gt;
  
  
  SDD: pros, contras y la estrategia que realmente funciona
&lt;/h2&gt;

&lt;p&gt;Hay un tipo de desarrollador que me resulta muy familiar.&lt;/p&gt;

&lt;p&gt;Abre un tutorial de IA. Ve al tipo construir una app desde cero en 20 minutos. Se emociona. Abre su proyecto real. Y se da cuenta de que lo que tiene delante no tiene nada que ver con lo que acaba de ver.&lt;/p&gt;

&lt;p&gt;Porque su proyecto no es un repo vacío con carpetas bien ordenadas.&lt;/p&gt;

&lt;p&gt;Su proyecto es un monolito de cuatro años con lógica de negocio enterrada en helpers que nadie sabe quién escribió. Con una tabla de base de datos que tiene 47 columnas y ninguna tiene comentario. Con tres formas distintas de hacer la misma cosa según en qué parte del código estés. Con una carpeta llamada &lt;code&gt;utils&lt;/code&gt; que tiene 3.000 líneas.&lt;/p&gt;

&lt;p&gt;Eso es un proyecto brownfield.&lt;/p&gt;

&lt;p&gt;Y la IA no te lo va a limpiar sola.&lt;/p&gt;

&lt;p&gt;Pero hay algo que sí puedes hacer. Y funciona mejor de lo que parece.&lt;/p&gt;




&lt;h2&gt;
  
  
  Primero, el problema real
&lt;/h2&gt;

&lt;p&gt;Cuando la gente descubre Spec-Driven Development, lo primero que piensa es: "genial, pero eso es para proyectos nuevos."&lt;/p&gt;

&lt;p&gt;Es un razonamiento lógico. SDD habla de escribir la spec antes de empezar. En un brownfield ya empezaste hace años. El barco ya está en el agua. No puedes volver al puerto a hacer los planos.&lt;/p&gt;

&lt;p&gt;Esa lógica tiene un fallo.&lt;/p&gt;

&lt;p&gt;SDD no es un método para proyectos. Es un método para cambios.&lt;/p&gt;

&lt;p&gt;Y tú, trabajes en el proyecto que trabajes, vas a hacer cambios. Hoy, mañana, la semana que viene. Cada vez que añades una feature, cada vez que corriges un bug, cada vez que tocas algo que otro construyó y que no entiendes del todo.&lt;/p&gt;

&lt;p&gt;Ahí es exactamente donde entra SDD. No en el origen del proyecto. En cada cambio que haces a partir de ahora.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que SDD sí resuelve en brownfield
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;El sistema deja de ser una caja negra.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando escribes una spec sobre algo que ya existe, el primer paso es describir cómo funciona &lt;em&gt;ahora&lt;/em&gt;. No cómo debería funcionar. Cómo funciona.&lt;/p&gt;

&lt;p&gt;Ese ejercicio solo ya tiene un valor enorme.&lt;/p&gt;

&lt;p&gt;Muchos equipos llevan años trabajando en sistemas que nadie sabe describir con precisión. Hay intuición. Hay memoria histórica. Hay "creo que funciona así pero no estoy seguro." La spec obliga a convertir esa niebla en texto.&lt;/p&gt;

&lt;p&gt;Y el texto se queda. El texto se versiona. El texto lo puede leer el agente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El agente tiene una frontera.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El problema clásico del brownfield con IA: le pides al agente que cambie una cosa y te cambia cinco. Porque el agente no sabe qué está permitido tocar y qué no.&lt;/p&gt;

&lt;p&gt;Una spec con una sección "fuera de scope" resuelve eso de forma radical.&lt;/p&gt;

&lt;p&gt;El agente no adivina. Ejecuta lo que le das. Si le das una spec que dice explícitamente qué no entra en este cambio, el agente no lo toca. No porque sea listo. Porque tiene instrucciones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las regresiones bajan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No desaparecen. Bajan.&lt;/p&gt;

&lt;p&gt;Si la spec describe el comportamiento que tiene que seguir siendo verdad cuando el cambio esté hecho, el agente tiene un criterio para verificar su propio trabajo. "¿Lo que he implementado cumple lo que dice la spec?" Es una pregunta que el agente puede responder.&lt;/p&gt;

&lt;p&gt;Sin spec, el agente no tiene criterio. Tiene intuición. Y la intuición de un agente en código heredado que no entiende del todo es una fuente inagotable de bugs que aparecen tres semanas después.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El conocimiento se acumula.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este es el punto que más se subestima.&lt;/p&gt;

&lt;p&gt;Cada mini-spec que escribes es documentación que se queda en el repo. No en Notion. No en Confluence. No en la cabeza del desarrollador que lleva más tiempo en el equipo. En el repo, junto al código que describe.&lt;/p&gt;

&lt;p&gt;Con suficientes cambios bien especificados, el sistema empieza a tener memoria. Los módulos más críticos tienen spec. Los flujos más usados están descritos. El onboarding de alguien nuevo mejora sin que nadie lo haya planificado.&lt;/p&gt;

&lt;p&gt;Es un efecto secundario del método. Pero es uno de los más valiosos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que SDD no resuelve
&lt;/h2&gt;

&lt;p&gt;Aquí hay que ser honesto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El código mal estructurado sigue siendo malo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Una spec no refactoriza nada. Si tienes un módulo de 800 líneas con efectos secundarios en tres capas distintas, la spec te ayuda a acotar el cambio. Pero el módulo sigue siendo un desastre.&lt;/p&gt;

&lt;p&gt;SDD no es una excusa para no hacer el trabajo de limpieza cuando es necesario. Es una herramienta para hacer cambios seguros. No es la misma cosa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sin tests, la spec flota en el aire.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La spec describe el comportamiento esperado. Pero si no hay nada que verifique automáticamente que ese comportamiento es real, la spec es solo texto.&lt;/p&gt;

&lt;p&gt;Los tests y la spec se complementan. La spec dice qué tiene que ser verdad. Los tests comprueban que lo es. Si tienes spec pero no tienes tests, tienes la mitad del sistema. La mitad útil para pensar. Pero no la mitad que te avisa cuando algo se rompe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentar retroactivamente un sistema grande es trabajo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No hay atajo aquí. Si tienes un monolito de cinco años y quieres tener spec de todo antes de tocar nada, vas a tardar meses. Y probablemente no lo vas a terminar nunca porque mientras escribes specs el negocio sigue pidiendo features.&lt;/p&gt;

&lt;p&gt;La solución no es intentar especificarlo todo de golpe. La solución es no intentarlo.&lt;/p&gt;




&lt;h2&gt;
  
  
  La estrategia que realmente funciona
&lt;/h2&gt;

&lt;p&gt;No especifiques el sistema. Especifica el cambio.&lt;/p&gt;

&lt;p&gt;Es la diferencia entre un proyecto paralizado y uno que avanza.&lt;/p&gt;

&lt;p&gt;El flujo es este:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Identifica exactamente qué vas a tocar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No el sistema. No el módulo entero si solo necesitas cambiar una parte de él. La unidad mínima de trabajo que tiene sentido hacer de forma independiente.&lt;/p&gt;

&lt;p&gt;Cuanto más pequeño sea el alcance, más fácil es especificarlo. Y cuanto más fácil es especificarlo, más probable es que lo hagas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Escribe el estado actual antes que el estado deseado&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este es el paso que más gente se salta. Y es el más importante en brownfield.&lt;/p&gt;

&lt;p&gt;Antes de escribir lo que quieres que haga el sistema, escribe lo que hace ahora. Con precisión. No "valida los datos del formulario." Qué valida, cuándo, qué pasa cuando falla, qué no valida aunque debería.&lt;/p&gt;

&lt;p&gt;Ese ejercicio tiene dos efectos. Primero, te obliga a entender de verdad lo que vas a tocar antes de tocarlo. Segundo, le da al agente el contexto que necesita para no romper lo que ya funciona.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Estado actual&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; El formulario valida formato de email solo en el cliente (regex básico)
&lt;span class="p"&gt;-&lt;/span&gt; Si el campo está vacío y el usuario hace submit, no pasa nada visible
&lt;span class="p"&gt;-&lt;/span&gt; El servidor acepta cualquier payload sin validar

&lt;span class="gu"&gt;## Estado deseado&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; El servidor valida el email antes de persistir (formato + dominio real)
&lt;span class="p"&gt;-&lt;/span&gt; El cliente muestra un mensaje de error específico por campo al hacer submit
&lt;span class="p"&gt;-&lt;/span&gt; Los errores de servidor se mapean al campo correspondiente en el formulario

&lt;span class="gu"&gt;## Fuera de scope&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No se cambia el diseño visual del formulario
&lt;span class="p"&gt;-&lt;/span&gt; No se añaden campos nuevos
&lt;span class="p"&gt;-&lt;/span&gt; No se toca la lógica de envío de emails posterior al submit
&lt;span class="p"&gt;-&lt;/span&gt; No se migran registros existentes con emails inválidos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sección "fuera de scope" no es opcional. Es la que protege el trabajo que ya funciona.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Dale al agente contexto preciso, no contexto abundante&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El error más común: pegar todo el repo en el contexto y esperar que el agente entienda qué es relevante.&lt;/p&gt;

&lt;p&gt;El agente trabaja mejor con menos contexto bien elegido que con todo el contexto mal filtrado.&lt;/p&gt;

&lt;p&gt;Dale los archivos que afectan directamente al cambio. El componente del formulario. El endpoint del servidor. El schema de validación si existe. Nada más.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Verifica contra la spec, no contra la intuición&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando el agente termine, la pregunta no es "¿parece bien?" La pregunta es "¿hace lo que dice la spec?"&lt;/p&gt;

&lt;p&gt;Esa es la diferencia entre revisar con criterio y revisar con fe.&lt;/p&gt;

&lt;p&gt;Si algo de lo que describe la spec no está implementado, vuelves al agente con esa diferencia concreta. No con "esto no está bien." Con "la spec dice X y el código hace Y."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. La spec se queda en el repo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No la borres cuando el cambio esté hecho. No es documentación temporal. Es la memoria del sistema.&lt;/p&gt;

&lt;p&gt;La persona que toque ese módulo dentro de seis meses — que probablemente seas tú — va a abrir ese archivo y va a entender en dos minutos lo que de otro modo le habría costado dos horas de arqueología de código.&lt;/p&gt;




&lt;h2&gt;
  
  
  El efecto acumulado
&lt;/h2&gt;

&lt;p&gt;Aquí está la parte que más me gusta de este método en brownfield.&lt;/p&gt;

&lt;p&gt;No necesitas un sprint de documentación. No necesitas parar todo para "pagar la deuda técnica." No necesitas convencer a nadie de que hay que refactorizar antes de avanzar.&lt;/p&gt;

&lt;p&gt;Solo necesitas que cada cambio que hagas a partir de ahora sea un cambio con spec.&lt;/p&gt;

&lt;p&gt;Eso es todo.&lt;/p&gt;

&lt;p&gt;Con el tiempo, el sistema que era opaco empieza a tener zonas claras. Los módulos que más se tocan son los que más spec tienen. Que resulta ser exactamente el conocimiento más valioso: el de las partes que cambian, no el de las partes que llevan tres años sin tocarse.&lt;/p&gt;

&lt;p&gt;El brownfield no desaparece. Pero deja de ser un problema sin solución.&lt;/p&gt;

&lt;p&gt;Se convierte en un sistema que se va documentando solo, cambio a cambio, a medida que crece.&lt;/p&gt;




&lt;h2&gt;
  
  
  Una última cosa
&lt;/h2&gt;

&lt;p&gt;El método que describo aquí — spec antes del cambio, estado actual antes del estado deseado, contexto preciso al agente, verificación contra spec — es el núcleo de lo que enseño en mi curso.&lt;/p&gt;

&lt;p&gt;Lo construí trabajando con Claude Code en proyectos reales, no en demos. Y lo he aplicado exactamente en el tipo de proyecto del que hablo aquí: código heredado, sin documentación, con decisiones que nadie recuerda haber tomado.&lt;/p&gt;

&lt;p&gt;Si quieres ver cómo se aplica esto en proyectos reales, tengo un &lt;a href="https://www.udemy.com/course/construye-con-ia-de-la-idea-al-producto-con-claude-code/?referralCode=AECD9EA3796054DEDD5D" rel="noopener noreferrer"&gt;curso donde lo cubro en detalle&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bezael Pérez — &lt;a href="https://dominicode.com" rel="noopener noreferrer"&gt;DominiCode&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>El método para construir con agentes de IA</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Mon, 25 May 2026 08:31:15 +0000</pubDate>
      <link>https://dev.to/bezael/el-metodo-para-construir-con-agentes-de-ia-2kkf</link>
      <guid>https://dev.to/bezael/el-metodo-para-construir-con-agentes-de-ia-2kkf</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m" class="crayons-story__hidden-navigation-link"&gt;BMAD: el método para construir con agentes de IA como si tuvieras un equipo&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bezael" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69894%2F8c8240fd-1dd9-4fbe-8277-26c618359d1b.jpg" alt="bezael profile" class="crayons-avatar__image" width="800" height="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bezael" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Bezael Pérez
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Bezael Pérez
                
              
              &lt;div id="story-author-preview-content-3747576" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bezael" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69894%2F8c8240fd-1dd9-4fbe-8277-26c618359d1b.jpg" class="crayons-avatar__image" alt="" width="800" height="1200"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Bezael Pérez&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m" id="article-link-3747576"&gt;
          BMAD: el método para construir con agentes de IA como si tuvieras un equipo
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/productivity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;productivity&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>agents</category>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>spanish</category>
    </item>
    <item>
      <title>BMAD: el método para construir con agentes de IA como si tuvieras un equipo</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Mon, 25 May 2026 08:30:42 +0000</pubDate>
      <link>https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m</link>
      <guid>https://dev.to/bezael/bmad-el-metodo-para-construir-con-agentes-de-ia-como-si-tuvieras-un-equipo-4i0m</guid>
      <description>&lt;p&gt;La mayoría de devs usa la IA como un buscador muy listo.&lt;/p&gt;

&lt;p&gt;Le hacen una pregunta. Obtienen una respuesta. Le hacen otra. Y así durante horas,&lt;br&gt;
dando vueltas al mismo problema sin llegar a ningún lado.&lt;/p&gt;

&lt;p&gt;Eso no es construir con IA. Es improvisar con IA.&lt;/p&gt;

&lt;p&gt;Y hay una diferencia enorme.&lt;/p&gt;


&lt;h2&gt;
  
  
  Qué es BMAD
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BMAD&lt;/strong&gt; son las siglas de &lt;em&gt;Breakthrough Method for Agile AI-Driven Development&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;No es un framework más. Es una forma distinta de pensar cómo trabajar con agentes.&lt;/p&gt;

&lt;p&gt;La premisa es simple: un solo agente que hace de todo es como contratar a alguien&lt;br&gt;
y pedirle que sea product manager, arquitecto, desarrollador y QA al mismo tiempo.&lt;/p&gt;

&lt;p&gt;Nadie rinde bien así. Los modelos tampoco.&lt;/p&gt;

&lt;p&gt;BMAD divide el trabajo entre agentes especializados, igual que un equipo real.&lt;/p&gt;


&lt;h2&gt;
  
  
  Los cuatro roles
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;th&gt;Responsabilidad&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Business Analyst&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entiende el problema, captura requisitos, escribe historias de usuario&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define el producto, prioriza el backlog, toma decisiones de alcance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Diseña la arquitectura, elige el stack, define los contratos entre módulos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Implementa el código con todo el contexto ya resuelto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cada agente tiene su propio system prompt, su propio foco, sus propias responsabilidades.&lt;/p&gt;

&lt;p&gt;No improvisa. Sabe exactamente para qué existe.&lt;/p&gt;


&lt;h2&gt;
  
  
  Un ejemplo real, paso a paso
&lt;/h2&gt;

&lt;p&gt;Supón que quieres construir una app de seguimiento de hábitos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sin ningún método:&lt;/strong&gt; abres el chat y escribes "hazme una app de hábitos con React".&lt;br&gt;
La IA genera algo genérico. No era lo que tenías en mente. Ajustas el prompt.&lt;br&gt;
Otras dos horas. Mismo resultado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Con BMAD&lt;/strong&gt;, el flujo es completamente distinto.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 1 — El Business Analyst entra primero
&lt;/h3&gt;

&lt;p&gt;No genera código. Hace preguntas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Eres un Business Analyst experto. Tu trabajo es entender el problema
antes de que se escriba una sola línea de código.

Pregunta al usuario:
- ¿Qué problema concreto resuelve esta app?
- ¿Quién la va a usar y en qué contexto?
- ¿Qué tiene que hacer sí o sí para ser útil desde el día uno?
- ¿Qué queda fuera del MVP?

No asumas nada. No propongas soluciones. Solo escucha y documenta.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El output es un documento de requisitos. Claro. Sin ambigüedad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 2 — El Product Manager toma ese documento
&lt;/h3&gt;

&lt;p&gt;Define las features con criterios de aceptación concretos.&lt;/p&gt;

&lt;p&gt;No "el usuario puede ver sus hábitos". Sino: &lt;em&gt;"el usuario ve un listado de hábitos activos&lt;br&gt;
ordenados por frecuencia, con indicador visual del streak actual"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Esa diferencia parece pequeña. En la implementación es enorme.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 3 — El Architect diseña antes de que se escriba código
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Eres un Architect de software. Recibes el PRD del Product Manager
y tu trabajo es definir:

- Estructura de carpetas y módulos
- Modelo de datos (entidades, relaciones, campos)
- Contratos entre capas (qué expone cada módulo, qué consume)
- Stack técnico justificado

No escribas implementación. Escribe decisiones.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El output es un documento de arquitectura. El Developer lo recibe como contexto fijo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 4 — El Developer implementa con todo claro
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Eres un Developer. Tienes el PRD, la arquitectura y las historias de usuario.

Tu trabajo es implementar el módulo de autenticación siguiendo exactamente
las decisiones de arquitectura documentadas.

Si encuentras una ambigüedad, para y pregunta. No asumas.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Módulo a módulo. Con criterios de aceptación. Con arquitectura definida.&lt;/p&gt;

&lt;p&gt;Al final tienes un producto que alguien pensó antes de construir.&lt;/p&gt;




&lt;h2&gt;
  
  
  Por qué funciona mejor que hablar con un solo agente
&lt;/h2&gt;

&lt;p&gt;Los modelos rinden mejor con un rol delimitado.&lt;/p&gt;

&lt;p&gt;Un agente que solo hace arquitectura toma mejores decisiones de diseño que uno al que&lt;br&gt;
le pides arquitectura, código, documentación y tests en el mismo chat.&lt;/p&gt;

&lt;p&gt;El contexto se contamina. El foco se pierde. La calidad baja.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La especialización no es solo para humanos.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  La conexión con la especificación
&lt;/h2&gt;

&lt;p&gt;Lo que BMAD hace de forma implícita — separar el &lt;em&gt;qué&lt;/em&gt; del &lt;em&gt;cómo&lt;/em&gt; antes de escribir&lt;br&gt;
código — tiene un nombre en ingeniería de software: desarrollo orientado a especificación.&lt;/p&gt;

&lt;p&gt;Si quieres llevar esta idea más lejos y aplicarla en tu flujo de trabajo con IA de forma&lt;br&gt;
sistemática, hay una metodología que lo formaliza: &lt;strong&gt;Spec-Driven Development (SDD)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;La idea es la misma: antes de que un agente genere una sola línea de código, existe un&lt;br&gt;
documento de especificación claro que define el qué, el quién, los flujos y las decisiones&lt;br&gt;
de arquitectura. El agente developer recibe eso como contexto cerrado, no como un chat abierto.&lt;/p&gt;

&lt;p&gt;Tengo un libro corto sobre esto — &lt;a href="https://leanpub.com/sdd" rel="noopener noreferrer"&gt;SDD: Construye con control&lt;/a&gt; — si&lt;br&gt;
quieres ver cómo estructura esa especificación en la práctica.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cómo empezar con BMAD
&lt;/h2&gt;

&lt;p&gt;BMAD es &lt;strong&gt;open source&lt;/strong&gt; en GitHub: busca &lt;code&gt;[bmad-method](https://github.com/bmad-code-org/bmad-method)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Puedes tomarlo tal cual, adaptarlo a tu stack o usarlo como referencia para construir&lt;br&gt;
tu propio sistema de agentes. Funciona con Claude, con GPT, con Gemini.&lt;/p&gt;

&lt;p&gt;El modelo importa menos que el sistema.&lt;/p&gt;




&lt;p&gt;Los mejores builders que conozco no tienen mejores prompts.&lt;/p&gt;

&lt;p&gt;Tienen mejor estructura.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cómo integrar OpenSpec con Claude Code paso a paso</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Mon, 04 May 2026 12:05:26 +0000</pubDate>
      <link>https://dev.to/bezael/como-integrar-openspec-con-claude-code-paso-a-paso-5ej3</link>
      <guid>https://dev.to/bezael/como-integrar-openspec-con-claude-code-paso-a-paso-5ej3</guid>
      <description>&lt;p&gt;Si usas Claude Code para programar ya sabes lo que pasa: abres una nueva sesión y el agente vuelve a improvisar. El contexto de la sesión anterior desapareció. Le describes la feature de nuevo, asume cosas distintas, y acabas corrigiendo código que nadie pidió.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenSpec resuelve exactamente eso.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Es un CLI open-source que inserta una capa de especificación versionada dentro de tu proyecto. Claude Code la lee en cualquier sesión, sabe qué se acordó construir, y trabaja contra esa spec — no contra tus prompts.&lt;/p&gt;

&lt;p&gt;En este artículo te muestro la integración completa, paso a paso, con una demo real.&lt;/p&gt;




&lt;h2&gt;
  
  
  Qué es OpenSpec (en 2 párrafos)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/course/construye-con-ia-de-la-idea-al-producto-con-claude-code/" rel="noopener noreferrer"&gt;OpenSpec&lt;/a&gt; es un toolkit de &lt;em&gt;spec-driven development&lt;/em&gt; para agentes de IA. Antes de que el agente toque el código, tú y él acuerdan en un documento estructurado qué se va a construir, por qué y cómo. A partir de ahí el agente trabaja contra esa especificación.&lt;/p&gt;

&lt;p&gt;Lo que lo diferencia de SpecKit (GitHub) o BMAD Method es que está diseñado para proyectos que &lt;strong&gt;ya existen y evolucionan&lt;/strong&gt; — proyectos brownfield. Usa marcadores de cambio (&lt;code&gt;ADDED&lt;/code&gt;, &lt;code&gt;MODIFIED&lt;/code&gt;, &lt;code&gt;REMOVED&lt;/code&gt;) para registrar exactamente qué cambia sobre el código existente. Es ligero (~250 líneas de spec vs ~800 de SpecKit) y se integra mediante slash commands directamente en &lt;a href="https://www.dominicode.com/spec-driven-development" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requisitos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20+&lt;/li&gt;
&lt;li&gt;Claude Code instalado&lt;/li&gt;
&lt;li&gt;Un proyecto existente (la demo usa una API en TypeScript)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Paso 1 — Instalación
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; openspec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verifica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# openspec/1.3.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Paso 2 — Inicializar en tu proyecto
&lt;/h2&gt;

&lt;p&gt;Ve a la raíz de tu proyecto y ejecuta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;mi-proyecto
openspec init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aparece un selector interactivo con todas las herramientas compatibles. Selecciona &lt;strong&gt;Claude Code&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Select your AI coding tool:
  GitHub Copilot
  Cursor
❯ Claude Code
  Kilo Code
  Windsurf
  (more...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al confirmar, OpenSpec genera esta estructura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openspec/
  project.md       ← contexto general del proyecto
  specs/           ← estado actual de las specs (se actualiza con cada archive)
  changes/         ← propuestas en curso
  archive/         ← historial de cambios completados
agents.md          ← instrucciones para Claude Code (NO modificar manualmente)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Paso 3 — Llenar project.md
&lt;/h2&gt;

&lt;p&gt;Este paso es el más importante y el que más se saltea. Abre &lt;code&gt;openspec/project.md&lt;/code&gt; y rellena el contexto real de tu proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Mi API de Gestión de Tareas&lt;/span&gt;

&lt;span class="gu"&gt;## Stack técnico&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Runtime: Node.js 22 + TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; Framework: Fastify
&lt;span class="p"&gt;-&lt;/span&gt; ORM: Prisma + PostgreSQL
&lt;span class="p"&gt;-&lt;/span&gt; Testing: Vitest

&lt;span class="gu"&gt;## Convenciones&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Nombres de archivos: kebab-case
&lt;span class="p"&gt;-&lt;/span&gt; Variables de entorno: siempre validar con zod al inicio
&lt;span class="p"&gt;-&lt;/span&gt; Errores: usar clase AppError con statusCode y message
&lt;span class="p"&gt;-&lt;/span&gt; No usar &lt;span class="sb"&gt;`any`&lt;/span&gt; — tipado estricto

&lt;span class="gu"&gt;## Estructura de carpetas&lt;/span&gt;
src/
  routes/          ← definición de endpoints
  services/        ← lógica de negocio
  middleware/       ← guards, validación
  lib/             ← utilidades compartidas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Por qué importa:&lt;/strong&gt; Claude Code lee este archivo al inicio de cada sesión. Con buen contexto aquí, las propuestas que genera son precisas desde la primera iteración.&lt;/p&gt;




&lt;h2&gt;
  
  
  Paso 4 — Entender agents.md
&lt;/h2&gt;

&lt;p&gt;OpenSpec generó un &lt;code&gt;agents.md&lt;/code&gt; en la raíz. Claude Code lo lee automáticamente como instrucción de proyecto, igual que un &lt;code&gt;CLAUDE.md&lt;/code&gt;. Dentro están definidos los tres slash commands que vas a usar:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Comando&lt;/th&gt;
&lt;th&gt;Qué hace&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/openspec-proposal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activa modo planificación. Claude pregunta sobre la feature y genera los archivos de propuesta.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/openspec-apply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Implementa el cambio aprobado, tarea por tarea, referenciando la spec.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/openspec-archive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cierra el cambio, actualiza las specs actuales y limpia &lt;code&gt;changes/&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Si ya tienes un &lt;code&gt;CLAUDE.md&lt;/code&gt; en tu proyecto, no hay conflicto — Claude Code lee ambos archivos. Déjalos en la raíz.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Paso 5 — Demo completa: añadir autenticación JWT
&lt;/h2&gt;

&lt;p&gt;Vamos a añadir autenticación JWT a la API. Proyecto que ya existe, feature real.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 — Crear la propuesta
&lt;/h3&gt;

&lt;p&gt;En Claude Code, ejecuta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/openspec-proposal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code entra en modo planificación y empieza a hacer preguntas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenSpec Proposal Mode activated.

Tell me about the feature you want to build:
&amp;gt; Autenticación JWT — login, logout, refresh token, y proteger rutas existentes

What routes need protection?
&amp;gt; /tasks, /projects — todas las rutas excepto /auth/*

Token strategy?
&amp;gt; Access token (15min) + Refresh token (7 días) en httpOnly cookie

Algorithm preference?
&amp;gt; HS256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al responder, genera tres archivos en &lt;code&gt;openspec/changes/jwt-auth/&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;proposal.md&lt;/code&gt;&lt;/strong&gt; — descripción del cambio y decisiones de diseño:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# JWT Authentication&lt;/span&gt;

&lt;span class="gu"&gt;## Objetivo&lt;/span&gt;
Proteger las rutas /tasks y /projects con autenticación JWT.

&lt;span class="gu"&gt;## Decisiones&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Access token: HS256, 15 minutos, en Authorization header
&lt;span class="p"&gt;-&lt;/span&gt; Refresh token: 7 días, httpOnly cookie
&lt;span class="p"&gt;-&lt;/span&gt; Secret: JWT_SECRET en variables de entorno (validar con zod al init)
&lt;span class="p"&gt;-&lt;/span&gt; Logout: blacklist en Redis (TTL = expiración del access token)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;tasks.md&lt;/code&gt;&lt;/strong&gt; — lista de tareas atómicas en orden de dependencia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Tasks: jwt-auth&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] 1. Instalar dependencias: jsonwebtoken, @types/jsonwebtoken
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 2. Crear lib/jwt.ts con sign, verify, decode
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 3. Crear servicio auth.service.ts con login y refresh
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 4. Crear middleware/auth.guard.ts que valida el token
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 5. Añadir rutas POST /auth/login y POST /auth/refresh
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 6. Proteger rutas /tasks/&lt;span class="ge"&gt;* y /projects/*&lt;/span&gt; con el guard
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 7. Añadir JWT_SECRET a validación de env vars
&lt;span class="p"&gt;-&lt;/span&gt; [ ] 8. Tests unitarios para lib/jwt.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;spec.md&lt;/code&gt;&lt;/strong&gt; — cambios sobre las specs actuales con marcadores delta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Spec delta: jwt-auth&lt;/span&gt;

[ADDED] lib/jwt.ts — utilidades de sign/verify
[ADDED] services/auth.service.ts
[ADDED] middleware/auth.guard.ts
[ADDED] routes/auth.route.ts
[MODIFIED] routes/tasks.route.ts — añadir preHandler: authGuard
[MODIFIED] routes/projects.route.ts — añadir preHandler: authGuard
[MODIFIED] lib/env.ts — añadir JWT_SECRET a schema zod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 — Revisar y ajustar
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Este paso es el valor real de OpenSpec.&lt;/strong&gt; Revisa los archivos y corrige las asunciones del agente antes de que escriba código.&lt;/p&gt;

&lt;p&gt;En la revisión encontramos que el agente asumió Redis para la blacklist pero no tenemos Redis en el proyecto. Editamos &lt;code&gt;proposal.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Decisiones&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; ...
&lt;span class="p"&gt;-&lt;/span&gt; Logout: no blacklist por ahora — invalidación por expiración de token
  (se añadirá Redis en una feature separada)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valida que todo esté consistente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec validate
&lt;span class="c"&gt;# ✓ Spec is valid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3 — Implementar
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/openspec-apply jwt-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code lee &lt;code&gt;tasks.md&lt;/code&gt; y &lt;code&gt;spec.md&lt;/code&gt;, y ejecuta cada tarea en orden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[1/8] Installing dependencies...
[2/8] Creating lib/jwt.ts...
[3/8] Creating services/auth.service.ts...
[4/8] Creating middleware/auth.guard.ts...
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;[8/8] Writing tests for lib/jwt.ts...

✓ All tasks completed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El agente no improvisa nada fuera de la spec. Si necesita tomar una decisión que no estaba en el documento, te pregunta antes de proceder.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4 — Archivar
&lt;/h3&gt;

&lt;p&gt;Revisa el código generado. Cuando estés conforme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openspec archive jwt-auth &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ Specs updated
✓ Change moved to archive/jwt-auth/
✓ changes/ cleared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;openspec/specs/&lt;/code&gt; ahora refleja el estado actual con la autenticación incluida. &lt;code&gt;changes/&lt;/code&gt; está vacío y listo para la siguiente feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips para sacarle partido con Claude Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Combina con CLAUDE.md para reglas globales&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;agents.md&lt;/code&gt; gestiona el workflow de OpenSpec. Tu &lt;code&gt;CLAUDE.md&lt;/code&gt; puede tener reglas más generales del proyecto que aplican siempre, independientemente de OpenSpec. Se complementan bien.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Revisa siempre entre proposal y apply&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La revisión humana es la clave. Un agente bien orientado con una spec correcta produce código predecible. Un agente con una spec con errores produce los mismos errores a escala.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Usa &lt;code&gt;openspec validate&lt;/code&gt; antes de cada apply&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Detecta referencias rotas o inconsistencias en los archivos de spec antes de lanzar la implementación.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Features pequeñas, ciclos cortos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenSpec funciona mejor con cambios atómicos (una feature bien delimitada) que con cambios masivos. Si la lista de tareas supera 12-15 items, divide la feature en dos propuestas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Comitea openspec/ junto con el código&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Los archivos en &lt;code&gt;openspec/&lt;/code&gt; son documentación ejecutable. Versionalos en git. El &lt;code&gt;archive/&lt;/code&gt; es especialmente valioso — es el historial de decisiones de diseño del proyecto.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cuándo usarlo y cuándo no
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vale la pena si:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El proyecto tiene más de unas semanas de vida y sigue creciendo&lt;/li&gt;
&lt;li&gt;Las features tienen 5+ tareas de implementación&lt;/li&gt;
&lt;li&gt;El agente ha roto cosas antes por falta de contexto&lt;/li&gt;
&lt;li&gt;Quieres un historial auditable de qué se construyó y por qué&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Es overkill si:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Es un proyecto de demo o prueba de concepto&lt;/li&gt;
&lt;li&gt;La tarea es una sola pieza pequeña (un componente, un endpoint sencillo)&lt;/li&gt;
&lt;li&gt;Ya tienes un flujo que funciona bien con solo un &lt;code&gt;CLAUDE.md&lt;/code&gt; detallado&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;OpenSpec + Claude Code resuelve el problema más frustrante del desarrollo con agentes: el contexto perdido entre sesiones. La inversión inicial (5-10 minutos de setup) se recupera en la primera feature donde el agente no improvisa nada que no estabas esperando.&lt;/p&gt;

&lt;p&gt;El repo está en &lt;a href="https://github.com/Fission-AI/OpenSpec" rel="noopener noreferrer"&gt;github.com/Fission-AI/OpenSpec&lt;/a&gt; y la documentación oficial en &lt;a href="https://intent-driven.dev/knowledge/openspec/" rel="noopener noreferrer"&gt;intent-driven.dev/knowledge/openspec&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tutorial en video con demo completa en el canal de YouTube → &lt;a href="https://dominicode.com" rel="noopener noreferrer"&gt;dominicode.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>openspec</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Spec-Driven Development: el método para construir con IA sin perder el control</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Thu, 09 Apr 2026 07:40:32 +0000</pubDate>
      <link>https://dev.to/bezael/spec-driven-development-el-metodo-para-construir-con-ia-sin-perder-el-control-5ce5</link>
      <guid>https://dev.to/bezael/spec-driven-development-el-metodo-para-construir-con-ia-sin-perder-el-control-5ce5</guid>
      <description>&lt;p&gt;Tienes una idea. Le pides a Claude o Cursor que la construya. En 20 minutos tienes algo que funciona.&lt;/p&gt;

&lt;p&gt;Dos semanas después, el proyecto es una bomba de tiempo. Cada feature nueva rompe algo. Nadie sabe por qué el código hace lo que hace. Y cada prompt al agente sale peor que el anterior porque el contexto se ha ido acumulando sin orden.&lt;/p&gt;

&lt;p&gt;Eso no es un problema de la IA. Es un problema de proceso.&lt;/p&gt;




&lt;h2&gt;
  
  
  El vibe coding tiene un límite
&lt;/h2&gt;

&lt;p&gt;El vibe coding es real. Funciona. Para prototipos, para explorar ideas, para hacer algo pequeño en un fin de semana.&lt;/p&gt;

&lt;p&gt;Pero tiene un problema de escala.&lt;/p&gt;

&lt;p&gt;Cuando el proyecto crece, el conocimiento sobre él vive en tu cabeza. En el historial de chats con el agente. En comentarios dispersos. Cuando quieres cambiar algo, tienes que reconstruir el contexto desde cero cada vez.&lt;/p&gt;

&lt;p&gt;El agente ejecuta bien. El problema es que ejecuta lo que le dices. Y si lo que le dices es ambiguo, incompleto o contradictorio, el resultado también lo es.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spec-Driven Development (SDD)&lt;/strong&gt; &lt;a href="https://www.udemy.com/course/construye-con-ia-de-la-idea-al-producto-con-claude-code/?referralCode=AECD9EA3796054DEDD5D" rel="noopener noreferrer"&gt;resuelve exactamente eso&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Qué es SDD
&lt;/h2&gt;

&lt;p&gt;Una frase lo resume:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;La spec se convierte en el artefacto primario que dirige todo el trabajo posterior: implementación, tests, documentación, verificación.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En el desarrollo tradicional, el código es la fuente de verdad. La documentación sirve al código.&lt;/p&gt;

&lt;p&gt;SDD invierte esa relación.&lt;/p&gt;

&lt;p&gt;La spec no sirve al código. &lt;strong&gt;El código sirve a la spec.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defines el comportamiento esperado antes de que empiece la implementación. Ese comportamiento está escrito en un documento que el agente puede leer, seguir y contra el que puede verificar su trabajo.&lt;/p&gt;

&lt;p&gt;El agente ejecuta. Tú piensas. Cada uno en su sitio.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que SDD no es
&lt;/h2&gt;

&lt;p&gt;Antes de seguir, tres malentendidos comunes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No es documentación que nadie lee.&lt;/strong&gt; Una spec de SDD vive con el código, se versiona con el código, y cuando el proyecto cambia, la spec cambia. No es un PDF en Confluence que nadie ha abierto en dos años.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No es waterfall.&lt;/strong&gt; SDD describe el destino, no el camino. El destino puede cambiar. Lo que no cambia es el principio: antes de que el agente ejecute, alguien tiene que haber pensado qué se quiere conseguir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No mata la velocidad.&lt;/strong&gt; La velocidad que te dio la IA se queda. Lo que cambia es que ahora va en la dirección correcta.&lt;/p&gt;




&lt;h2&gt;
  
  
  Las 7 fases del flujo
&lt;/h2&gt;

&lt;p&gt;Matt Pocock publicó en 2025 un video sobre su flujo de trabajo con IA — más de 250k visualizaciones — donde describió siete momentos que ocurren en cualquier proyecto serio. Independientemente de la herramienta.&lt;/p&gt;

&lt;p&gt;Aquí están.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 1: Idea
&lt;/h3&gt;

&lt;p&gt;Todo empieza aquí. La trampa es ir demasiado rápido a la siguiente fase.&lt;/p&gt;

&lt;p&gt;Tienes la idea. La entiendes en tu cabeza. Quieres empezar.&lt;/p&gt;

&lt;p&gt;El problema: "la entiendes en tu cabeza" no significa que esté suficientemente definida para que un agente la ejecute bien. En tu cabeza hay contexto implícito, restricciones que das por sentadas, decisiones que no has tomado conscientemente.&lt;/p&gt;

&lt;p&gt;Una pregunta para salir de la fase 1: ¿puedo describir lo que quiero en dos frases que no sean ambiguas?&lt;/p&gt;

&lt;p&gt;Si la respuesta es no, todavía estás aquí.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 2: Research &lt;em&gt;(opcional)&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Si hay algo desconocido en el proyecto — una API nueva, una integración que nunca hiciste — necesitas entenderlo antes de especificar.&lt;/p&gt;

&lt;p&gt;Formato: un archivo &lt;code&gt;research.md&lt;/code&gt;. Temporal, vive mientras dura el sprint. No es documentación permanente. Es caché.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 3: Prototipo &lt;em&gt;(opcional)&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;El prototipo ocurre &lt;strong&gt;antes&lt;/strong&gt; de la spec, no después.&lt;/p&gt;

&lt;p&gt;Si le pides al agente que construya sin haber tocado el problema primero, el agente toma todas las decisiones de diseño, de UX, de arquitectura. El resultado puede ser perfectamente funcional y completamente distinto a lo que habrías elegido tú.&lt;/p&gt;

&lt;p&gt;El prototipo es rápido y desechable. Una vez que lo tienes, lo commiteas. La spec puede referenciarlo. Entonces sí: a especificar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 4: PRD (La spec)
&lt;/h3&gt;

&lt;p&gt;El Product Requirements Document. &lt;strong&gt;El artefacto central de todo el método.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El PRD describe el destino, no el viaje. Qué tiene que ser verdad cuando el trabajo esté hecho. Quién lo usa. Qué puede hacer. Qué no puede hacer. Qué asumes sobre el estado actual. Qué criterios vas a usar para verificar que está hecho.&lt;/p&gt;

&lt;p&gt;No cómo implementarlo.&lt;/p&gt;

&lt;p&gt;Un PRD bien escrito tiene menos de 500 palabras. Si es más largo, está describiendo implementación.&lt;/p&gt;

&lt;p&gt;Ejemplo mínimo de estructura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Objetivo&lt;/span&gt;
[Qué problema resuelve esta feature en una frase]

&lt;span class="gu"&gt;## Usuarios&lt;/span&gt;
[Quién lo usa y en qué contexto]

&lt;span class="gu"&gt;## Comportamiento esperado&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [El usuario puede hacer X]
&lt;span class="p"&gt;-&lt;/span&gt; [El sistema responde con Y]
&lt;span class="p"&gt;-&lt;/span&gt; [Cuando Z ocurre, el resultado es W]

&lt;span class="gu"&gt;## Fuera de scope&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Qué NO incluye esta versión]

&lt;span class="gu"&gt;## Assumptions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Qué das por sentado sobre el estado actual del sistema]

&lt;span class="gu"&gt;## Criterios de aceptación&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] [Verificable, concreto, testeable]
&lt;span class="p"&gt;-&lt;/span&gt; [ ] [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sección de &lt;code&gt;assumptions&lt;/code&gt; es la que más impacto tiene en la calidad del código generado. Es también la que más gente se salta.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 5: Kanban
&lt;/h3&gt;

&lt;p&gt;El PRD define el destino. El kanban define el camino.&lt;/p&gt;

&lt;p&gt;El PRD se convierte en issues. Cada issue es una unidad de trabajo que el agente puede ejecutar de forma independiente.&lt;/p&gt;

&lt;p&gt;Dos principios para dividirlo:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertical slices.&lt;/strong&gt; Cada issue implementa algo de punta a punta: interfaz, lógica, base de datos. No issues por capa. No "issue de frontend" + "issue de backend". Eso genera dependencias artificiales y hace imposible verificar hasta que todos están hechos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tracer bullets.&lt;/strong&gt; Los unknowns más arriesgados van primero. Si no sabes si algo es técnicamente posible de la forma en que lo imaginaste, ese es el primer issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 6: Execution loop
&lt;/h3&gt;

&lt;p&gt;Aquí el agente toma el control.&lt;/p&gt;

&lt;p&gt;El loop es simple: el agente lee el issue, implementa, comenta y cierra. Pasa al siguiente.&lt;/p&gt;

&lt;p&gt;Lo que nunca es opcional: &lt;strong&gt;leer el código que el agente produce&lt;/strong&gt;. No para microgestionar. Para mantener el criterio sobre lo que se está construyendo. El agente ejecuta bien. Pero tú sigues siendo el responsable del resultado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 7: QA
&lt;/h3&gt;

&lt;p&gt;El trabajo está hecho. Pero no está terminado.&lt;/p&gt;

&lt;p&gt;QA significa verificar que el comportamiento real del sistema coincide con el comportamiento especificado en el PRD. El agente puede ayudar: dale el PRD y el código y pídele que genere un plan de QA. El plan lo genera él. Las pruebas las ejecutas tú.&lt;/p&gt;

&lt;p&gt;Los bugs y las inconsistencias que encuentres se convierten en nuevos issues. Vuelven a la fase 6.&lt;/p&gt;




&lt;h2&gt;
  
  
  El flujo en una imagen
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDEA
  └─ ¿Está suficientemente definida?

RESEARCH (si aplica)
  └─ research.md — caché temporal de lo desconocido

PROTOTIPO (si aplica)
  └─ Exploración rápida, desechable — commitear como referencia

PRD
  └─ Destino, no viaje
  └─ Máx. ~500 palabras — criterios de aceptación testeables

KANBAN
  └─ Vertical slices — tracer bullets primero
  └─ Blocking relationships — dependencias explícitas

EXECUTION LOOP
  └─ Agente ejecuta issue por issue
  └─ Humano lee el código producido

QA
  └─ Agente genera el plan — humano ejecuta
  └─ Bugs → nuevos issues → vuelve al loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Por qué ahora
&lt;/h2&gt;

&lt;p&gt;Thoughtworks, en su radar tecnológico de 2025, describió SDD como "una de las prácticas más importantes que emergieron con la IA generativa."&lt;/p&gt;

&lt;p&gt;GitHub SpecKit tiene más de 72.000 estrellas y soporte para más de veinte plataformas: Claude Code, GitHub Copilot, Amazon Q, Gemini CLI. No es un proyecto de nicho. Es infraestructura.&lt;/p&gt;

&lt;p&gt;¿Por qué importa ahora y no antes?&lt;/p&gt;

&lt;p&gt;Porque antes, las specs las leían desarrolladores humanos. Un desarrollador puede llenar huecos, hacer preguntas, pedir aclaraciones.&lt;/p&gt;

&lt;p&gt;Un agente no puede. Un agente rellena los huecos con lo que le parece más probable.&lt;/p&gt;

&lt;p&gt;Y lo que le parece más probable no siempre es lo que tú querías.&lt;/p&gt;

&lt;p&gt;Cuando el ejecutor es una IA, la calidad de las instrucciones importa más que nunca. SDD es la respuesta a eso.&lt;/p&gt;




&lt;h2&gt;
  
  
  Para empezar hoy
&lt;/h2&gt;

&lt;p&gt;Tengo una plantilla gratuita de PRD que puedes usar en tu próximo proyecto. Incluye la estructura completa, ejemplos de criterios de aceptación y la sección de assumptions que la mayoría se salta.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dominicode.com/spec-driven-development" rel="noopener noreferrer"&gt;dominicode.com/spec-driven-development&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bezael — &lt;a href="https://dominicode.com" rel="noopener noreferrer"&gt;DominiCode&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Tu app de vibe coding funciona. Eso es exactamente el problema.</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Wed, 08 Apr 2026 16:20:17 +0000</pubDate>
      <link>https://dev.to/bezael/tu-app-de-vibe-coding-funciona-eso-es-exactamente-el-problema-4k88</link>
      <guid>https://dev.to/bezael/tu-app-de-vibe-coding-funciona-eso-es-exactamente-el-problema-4k88</guid>
      <description>&lt;p&gt;Tu app funciona.&lt;/p&gt;

&lt;p&gt;La abriste en el navegador. Hiciste clic en los botones. Todo responde.&lt;/p&gt;

&lt;p&gt;Hiciste el deploy. Nadie se quejó.&lt;/p&gt;

&lt;p&gt;Y ahí está el problema.&lt;/p&gt;

&lt;p&gt;Porque "funciona" no significa lo mismo que "está bien". Y cuando le delegas el código a una IA sin revisarlo, la diferencia entre los dos puede ser un endpoint sin autenticación, un token JWT que no expira nunca, o una contraseña guardada en texto plano en tu base de datos.&lt;/p&gt;

&lt;p&gt;No porque la IA sea mala. Sino porque la IA optimiza para "hacer que funcione el prompt". No para "hacer que sea seguro en producción".&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que la IA no te dice cuando genera tu código
&lt;/h2&gt;

&lt;p&gt;La IA generó tu endpoint. Lo probaste con Postman. Devolvió 200.&lt;/p&gt;

&lt;p&gt;Pero no te dijo que cualquier usuario autenticado puede acceder a los datos de cualquier otro usuario simplemente cambiando el ID en la URL.&lt;/p&gt;

&lt;p&gt;La IA configuró tu autenticación con JWT. Los tokens se generan. Los usuarios entran.&lt;/p&gt;

&lt;p&gt;Pero no te dijo que esos tokens no tienen fecha de expiración. Que si uno se filtra, es válido para siempre.&lt;/p&gt;

&lt;p&gt;La IA puso CORS para que el frontend pudiera llamar al backend.&lt;/p&gt;

&lt;p&gt;Pero no te dijo que lo configuró con &lt;code&gt;origin: '*'&lt;/code&gt;. Que eso significa que cualquier dominio del mundo puede hacer peticiones a tu API.&lt;/p&gt;

&lt;p&gt;Esto no es teoría. Es lo que aparece cuando auditas una app de vibe coding. Una tras otra.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;/vibe-audit&lt;/code&gt; — 30 segundos para saber en qué lío estás
&lt;/h2&gt;

&lt;p&gt;Construí &lt;strong&gt;AI Workflow Kit&lt;/strong&gt; porque me cansé de revisar código generado por IA de manera manual y encontrar los mismos 20 problemas cada vez.&lt;/p&gt;

&lt;p&gt;La instalación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ai-workflow-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y luego, en cualquier proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/vibe-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso es todo. La skill escanea el proyecto completo, lee los archivos más críticos, y genera un reporte con severidad, evidencia concreta y el fix sugerido.&lt;/p&gt;

&lt;p&gt;No te dice "hay un problema de seguridad". Te dice exactamente dónde está, qué línea, qué puede pasar si no lo arreglas, y cómo arreglarlo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Los problemas que encuentra. Siempre.
&lt;/h2&gt;

&lt;p&gt;Después de auditar decenas de apps generadas con IA, hay 20 patrones que aparecen una y otra vez. Estos son los más comunes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Los críticos — los que pueden costarte el proyecto:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secretos hardcodeados.&lt;/strong&gt; La IA pone API keys, contraseñas y URLs de base de datos directamente en el código porque "funciona más rápido en el prompt". Aparece en casi el 100% de los proyectos generados sin revisión.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lo que encuentras&lt;/span&gt;
&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sk-...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sin validación de inputs.&lt;/strong&gt; Los endpoints confían ciegamente en &lt;code&gt;req.body&lt;/code&gt;. Cualquier usuario puede mandarte lo que quiera. Incluyendo &lt;code&gt;role: "admin"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;// el usuario se da a sí mismo rol de admin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IDOR — acceso a recursos de otros usuarios.&lt;/strong&gt; El endpoint &lt;code&gt;GET /api/orders/:id&lt;/code&gt; no verifica que ese pedido sea tuyo. Cambia el número en la URL. Accedes a los datos de cualquier otro usuario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contraseñas sin hashear.&lt;/strong&gt; No siempre. Pero ocurre, especialmente cuando el prompt fue vago o rápido.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT sin expiración.&lt;/strong&gt; &lt;code&gt;jwt.sign(payload, secret)&lt;/code&gt; sin &lt;code&gt;expiresIn&lt;/code&gt;. Token válido para siempre. Si se filtra — en logs, en localStorage, en cualquier sitio — el atacante tiene acceso permanente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack traces expuestos al cliente.&lt;/strong&gt; &lt;code&gt;res.json({ error: err.stack })&lt;/code&gt; en el manejador de errores. En producción esto le da al atacante rutas internas, versiones de librerías y lógica de la aplicación.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Los importantes — los que te explotan en producción:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Queries sin paginación.&lt;/strong&gt; &lt;code&gt;db.findMany()&lt;/code&gt; sin límite. En desarrollo con 10 registros, perfecto. En producción con 50.000, la base de datos se cae.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;APIs de terceros sin rate limiting.&lt;/strong&gt; La IA conecta tu app a OpenAI, Stripe, o cualquier API externa y llama directamente sin control. Un bot puede hacer miles de llamadas y arruinarte la factura o hacer que el proveedor banee tu cuenta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sin estados de loading o error en el frontend.&lt;/strong&gt; La IA genera el happy path perfecto. Si la API tarda o falla, la app se queda en blanco o explota en silencio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;console.logs de desarrollo en producción.&lt;/strong&gt; La IA logea todo para depurar. Esos logs exponen datos internos y contaminan los logs de producción.&lt;/p&gt;




&lt;p&gt;El reporte que genera &lt;code&gt;/vibe-audit&lt;/code&gt; tiene este formato:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Vibe Audit — nombre-del-proyecto&lt;/span&gt;
Auditado: fecha

&lt;span class="gu"&gt;## Resumen&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; 🔴 Críticos: N  (bloquear producción o son riesgos de seguridad)
&lt;span class="p"&gt;-&lt;/span&gt; 🟡 Importantes: N (afectan estabilidad o mantenibilidad)
&lt;span class="p"&gt;-&lt;/span&gt; 🔵 Mejoras: N   (deuda técnica, calidad)

&lt;span class="gu"&gt;## 🔴 Críticos&lt;/span&gt;

&lt;span class="gu"&gt;### Contraseñas sin hashear&lt;/span&gt;
&lt;span class="gs"&gt;**Dónde:**&lt;/span&gt; &lt;span class="sb"&gt;`src/auth/register.ts`&lt;/span&gt; línea 23
&lt;span class="gs"&gt;**Evidencia:**&lt;/span&gt;
[código problemático]
&lt;span class="gs"&gt;**Riesgo:**&lt;/span&gt; Si la base de datos se compromete, todas las contraseñas quedan expuestas en texto plano
&lt;span class="gs"&gt;**Fix sugerido:**&lt;/span&gt;
[código corregido]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No generalidades. Código concreto. Línea exacta. Fix aplicable.&lt;/p&gt;




&lt;h2&gt;
  
  
  El resto del kit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/vibe-audit&lt;/code&gt; es la razón de ser del kit. Pero no está solo.&lt;/p&gt;

&lt;p&gt;Una vez que tu app está auditada y los problemas críticos resueltos, el workflow se mantiene limpio con el resto de las herramientas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/commit&lt;/code&gt; — lee el diff real y genera un commit semántico. Sin copy-paste, sin "fix stuff"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/pr&lt;/code&gt; — PR con descripción, plan de tests y checklist de revisión&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/review @archivo&lt;/code&gt; — revisa bugs, seguridad y rendimiento con criterios de ingeniería real&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plan [tarea]&lt;/code&gt; — fuerza planificación antes de ejecutar. Para tareas complejas, un plan aprobado vale más que código rápido&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug [problema]&lt;/code&gt; — diagnóstico con hipótesis antes de proponer fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agentes especializados:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/frontend&lt;/code&gt; — componentes siguiendo el sistema de diseño del proyecto&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api&lt;/code&gt; — endpoints con validación, auth y manejo de errores&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/test&lt;/code&gt; — tests por comportamiento, no por implementación&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/refactor&lt;/code&gt; — mejora el código sin cambiar el comportamiento&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/docs&lt;/code&gt; — JSDoc, README, ADR según el contexto&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hooks automáticos&lt;/strong&gt; — sin activación, corren solos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bloquea comandos destructivos antes de que se ejecuten&lt;/li&gt;
&lt;li&gt;Escanea archivos staged buscando API keys antes de cada commit&lt;/li&gt;
&lt;li&gt;Formatea con Prettier o Biome después de cada edición&lt;/li&gt;
&lt;li&gt;Corre ESLint y devuelve los errores a Claude para que los corrija&lt;/li&gt;
&lt;li&gt;Notificación de escritorio cuando Claude termina (Mac/Linux/Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funciona con &lt;strong&gt;Claude Code&lt;/strong&gt;, &lt;strong&gt;Cursor&lt;/strong&gt; y &lt;strong&gt;GitHub Copilot&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  La diferencia entre vibe coding y vibe coding bien hecho
&lt;/h2&gt;

&lt;p&gt;El vibe coding no va a desaparecer. Ni debería.&lt;/p&gt;

&lt;p&gt;La velocidad es real. La productividad es real. Poder construir en horas lo que antes tardaba días es real.&lt;/p&gt;

&lt;p&gt;El problema no es la velocidad. El problema es confundir "rápido" con "hecho".&lt;/p&gt;

&lt;p&gt;Tu app necesita los dos. Velocidad para llegar al mercado. Y un &lt;code&gt;/vibe-audit&lt;/code&gt; para saber qué hay dentro antes de que alguien más lo descubra por ti.&lt;/p&gt;




&lt;p&gt;Una línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ai-workflow-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/bezael/ai-workflow-kit" rel="noopener noreferrer"&gt;https://github.com/bezael/ai-workflow-kit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>vibecoding</category>
      <category>ia</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Your vibe coding app works. That's exactly the problem.</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Wed, 08 Apr 2026 16:20:02 +0000</pubDate>
      <link>https://dev.to/bezael/your-vibe-coding-app-works-thats-exactly-the-problem-1omm</link>
      <guid>https://dev.to/bezael/your-vibe-coding-app-works-thats-exactly-the-problem-1omm</guid>
      <description>&lt;p&gt;Your app works.&lt;/p&gt;

&lt;p&gt;You opened it in the browser. Clicked the buttons. Everything responds.&lt;/p&gt;

&lt;p&gt;You deployed it. Nobody complained.&lt;/p&gt;

&lt;p&gt;And that's exactly the problem.&lt;/p&gt;

&lt;p&gt;Because "works" doesn't mean the same thing as "is correct". And when you delegate code to an AI without reviewing it, the gap between those two can be an endpoint with no authentication, a JWT token that never expires, or a password stored in plaintext in your database.&lt;/p&gt;

&lt;p&gt;Not because the AI is bad. But because the AI optimizes for "make the prompt work". Not for "make it safe in production".&lt;/p&gt;




&lt;h2&gt;
  
  
  What the AI doesn't tell you when it generates your code
&lt;/h2&gt;

&lt;p&gt;The AI generated your endpoint. You tested it with Postman. It returned 200.&lt;/p&gt;

&lt;p&gt;But it didn't tell you that any authenticated user can access any other user's data just by changing the ID in the URL.&lt;/p&gt;

&lt;p&gt;The AI set up your JWT authentication. Tokens are generated. Users log in.&lt;/p&gt;

&lt;p&gt;But it didn't tell you those tokens have no expiration date. That if one leaks, it's valid forever.&lt;/p&gt;

&lt;p&gt;The AI configured CORS so the frontend could call the backend.&lt;/p&gt;

&lt;p&gt;But it didn't tell you it set &lt;code&gt;origin: '*'&lt;/code&gt;. That means any domain in the world can make requests to your API.&lt;/p&gt;

&lt;p&gt;This isn't theory. It's what shows up when you audit a vibe-coded app. Every single time.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;/vibe-audit&lt;/code&gt; — 30 seconds to know what mess you're in
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;AI Workflow Kit&lt;/strong&gt; because I got tired of manually reviewing AI-generated code and finding the same 20 problems every time.&lt;/p&gt;

&lt;p&gt;Installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ai-workflow-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in any project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/vibe-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The skill scans the entire project, reads the most critical files, and generates a report with severity, concrete evidence, and the suggested fix.&lt;/p&gt;

&lt;p&gt;It doesn't tell you "there's a security problem". It tells you exactly where it is, which line, what can happen if you don't fix it, and how to fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problems it finds. Every time.
&lt;/h2&gt;

&lt;p&gt;After auditing dozens of AI-generated apps, there are 20 patterns that show up again and again. These are the most common:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The critical ones — the ones that can kill your project:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardcoded secrets.&lt;/strong&gt; The AI puts API keys, passwords, and database URLs directly in the code because "it works faster in the prompt". Shows up in almost 100% of projects generated without review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What you find&lt;/span&gt;
&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sk-...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No input validation.&lt;/strong&gt; Endpoints blindly trust &lt;code&gt;req.body&lt;/code&gt;. Any user can send you whatever they want. Including &lt;code&gt;role: "admin"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;// user gives themselves admin role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IDOR — access to other users' resources.&lt;/strong&gt; The &lt;code&gt;GET /api/orders/:id&lt;/code&gt; endpoint doesn't verify that order belongs to you. Change the number in the URL. You access any other user's data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unhashed passwords.&lt;/strong&gt; Not always. But it happens, especially when the prompt was vague or rushed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT without expiration.&lt;/strong&gt; &lt;code&gt;jwt.sign(payload, secret)&lt;/code&gt; without &lt;code&gt;expiresIn&lt;/code&gt;. Token valid forever. If it leaks — in logs, in localStorage, anywhere — the attacker has permanent access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack traces exposed to the client.&lt;/strong&gt; &lt;code&gt;res.json({ error: err.stack })&lt;/code&gt; in the error handler. In production this gives an attacker internal paths, library versions, and application logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The important ones — the ones that blow up in production:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Queries without pagination.&lt;/strong&gt; &lt;code&gt;db.findMany()&lt;/code&gt; without a limit. In development with 10 records, perfect. In production with 50,000, the database goes down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party APIs without rate limiting.&lt;/strong&gt; The AI connects your app to OpenAI, Stripe, or any external API and calls directly with no control. A bot can fire thousands of requests and wreck your bill or get your account banned by the provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No loading or error states in the frontend.&lt;/strong&gt; The AI generates the perfect happy path. If the API is slow or fails, the app goes blank or crashes silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development console.logs in production.&lt;/strong&gt; The AI logs everything for debugging. Those logs expose internal data and pollute your production logs.&lt;/p&gt;




&lt;p&gt;The report &lt;code&gt;/vibe-audit&lt;/code&gt; generates looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Vibe Audit — project-name&lt;/span&gt;
Audited: date

&lt;span class="gu"&gt;## Summary&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; 🔴 Critical: N  (block production or are security risks)
&lt;span class="p"&gt;-&lt;/span&gt; 🟡 Important: N (affect stability or maintainability)
&lt;span class="p"&gt;-&lt;/span&gt; 🔵 Improvements: N   (technical debt, quality)

&lt;span class="gu"&gt;## 🔴 Critical&lt;/span&gt;

&lt;span class="gu"&gt;### Unhashed passwords&lt;/span&gt;
&lt;span class="gs"&gt;**Where:**&lt;/span&gt; &lt;span class="sb"&gt;`src/auth/register.ts`&lt;/span&gt; line 23
&lt;span class="gs"&gt;**Evidence:**&lt;/span&gt;
[problematic code]
&lt;span class="gs"&gt;**Risk:**&lt;/span&gt; If the database is compromised, all passwords are exposed in plaintext
&lt;span class="gs"&gt;**Suggested fix:**&lt;/span&gt;
[corrected code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No vague warnings. Concrete code. Exact line. Actionable fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  The rest of the kit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/vibe-audit&lt;/code&gt; is the reason the kit exists. But it doesn't stand alone.&lt;/p&gt;

&lt;p&gt;Once your app is audited and the critical issues resolved, the rest of the tools keep the workflow clean:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/commit&lt;/code&gt; — reads the real diff and generates a semantic commit. No copy-paste, no "fix stuff"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/pr&lt;/code&gt; — PR with description, test plan, and reviewer checklist&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/review @file&lt;/code&gt; — reviews bugs, security, and performance with real engineering criteria&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plan [task]&lt;/code&gt; — forces planning before execution. For complex tasks, an approved plan is worth more than fast code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug [problem]&lt;/code&gt; — diagnosis with hypotheses before proposing fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Specialized agents:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/frontend&lt;/code&gt; — components following the project's design system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api&lt;/code&gt; — endpoints with validation, auth, and error handling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/test&lt;/code&gt; — behavior-driven tests, not implementation tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/refactor&lt;/code&gt; — improves code without changing behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/docs&lt;/code&gt; — JSDoc, README, or ADR depending on context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Automatic hooks&lt;/strong&gt; — no activation needed, they just run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocks destructive commands before they execute&lt;/li&gt;
&lt;li&gt;Scans staged files for API keys before every commit&lt;/li&gt;
&lt;li&gt;Formats with Prettier or Biome after every edit&lt;/li&gt;
&lt;li&gt;Runs ESLint and feeds errors back to Claude to fix&lt;/li&gt;
&lt;li&gt;Desktop notification when Claude finishes (Mac/Linux/Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Works with &lt;strong&gt;Claude Code&lt;/strong&gt;, &lt;strong&gt;Cursor&lt;/strong&gt;, and &lt;strong&gt;GitHub Copilot&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The difference between vibe coding and vibe coding done right
&lt;/h2&gt;

&lt;p&gt;Vibe coding isn't going away. And it shouldn't.&lt;/p&gt;

&lt;p&gt;The speed is real. The productivity is real. Being able to build in hours what used to take days is real.&lt;/p&gt;

&lt;p&gt;The problem isn't the speed. The problem is mistaking "fast" for "done".&lt;/p&gt;

&lt;p&gt;Your app needs both. Speed to reach the market. And a &lt;code&gt;/vibe-audit&lt;/code&gt; to know what's inside before someone else finds out for you.&lt;/p&gt;




&lt;p&gt;One line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ai-workflow-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/bezael/ai-workflow-kit" rel="noopener noreferrer"&gt;https://github.com/bezael/ai-workflow-kit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>vibecoding</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>De Senior Dev a Tech Lead: la transición donde más talento se pierde
¿Te ha pasado?</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Wed, 29 Oct 2025 18:08:40 +0000</pubDate>
      <link>https://dev.to/bezael/de-senior-dev-a-tech-lead-la-transicion-donde-mas-talento-se-pierdete-ha-pasado-15kf</link>
      <guid>https://dev.to/bezael/de-senior-dev-a-tech-lead-la-transicion-donde-mas-talento-se-pierdete-ha-pasado-15kf</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k" class="crayons-story__hidden-navigation-link"&gt;Por qué los desarrolladores que pasan a tech lead fracasan…&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bezael" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69894%2F8c8240fd-1dd9-4fbe-8277-26c618359d1b.jpg" alt="bezael profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bezael" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Bezael Pérez
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Bezael Pérez
                
              
              &lt;div id="story-author-preview-content-2969164" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bezael" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69894%2F8c8240fd-1dd9-4fbe-8277-26c618359d1b.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Bezael Pérez&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 28 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k" id="article-link-2969164"&gt;
          Por qué los desarrolladores que pasan a tech lead fracasan…
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/liderazgo"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;liderazgo&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tecnologia"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tecnologia&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/management"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;management&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>liderazgo</category>
      <category>tecnologia</category>
      <category>management</category>
    </item>
    <item>
      <title>Por qué los desarrolladores que pasan a tech lead fracasan…</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Tue, 28 Oct 2025 16:57:24 +0000</pubDate>
      <link>https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k</link>
      <guid>https://dev.to/bezael/por-que-los-desarrolladores-que-pasan-a-tech-lead-fracasan-305k</guid>
      <description>&lt;h3&gt;
  
  
  De Senior Dev a Tech Lead: la transición donde más talento se pierde
&lt;/h3&gt;

&lt;p&gt;¿Te ha pasado?&lt;br&gt;&lt;br&gt;
Eras un desarrollador senior brillante… y un día te dicen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A partir de ahora, serás Tech Lead.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lo tomas como un ascenso.&lt;br&gt;&lt;br&gt;
Pero en realidad, es un cambio de profesión.&lt;br&gt;&lt;br&gt;
Y aquí es donde muchos (muchísimos) desarrolladores fracasan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Porque siguen midiendo su valor con las métricas equivocadas.&lt;/p&gt;




&lt;h2&gt;
  
  
  El conflicto central
&lt;/h2&gt;

&lt;p&gt;Tu valor ya no está en tu código.&lt;/p&gt;

&lt;p&gt;Como &lt;strong&gt;Senior Dev&lt;/strong&gt;, te valoran por tu &lt;em&gt;output individual&lt;/em&gt;:&lt;br&gt;&lt;br&gt;
la calidad de tu código, lo rápido que resuelves problemas, lo bien que entregas.&lt;/p&gt;

&lt;p&gt;Como &lt;strong&gt;Tech Lead&lt;/strong&gt;, te valoran por el &lt;em&gt;output de tu equipo&lt;/em&gt;:&lt;br&gt;&lt;br&gt;
rendimiento colectivo, claridad, bienestar, producto final y alineación con negocio.&lt;/p&gt;

&lt;p&gt;El día que entiendes eso, &lt;strong&gt;todo cambia&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚨 Los 5 errores más comunes (y cómo evitarlos)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. El síndrome del héroe
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Es más rápido si lo hago yo.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Falla:&lt;/strong&gt; te conviertes en cuello de botella.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solución:&lt;/strong&gt; delega, confía y enseña. Tu trabajo ya no es escribir código, es que se escriba bien.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. El micromanagement disfrazado de “calidad”
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Code reviews de 50 comentarios, querer aprobarlo todo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Falla:&lt;/strong&gt; ahogas la autonomía del equipo.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solución:&lt;/strong&gt; define estándares, automatiza lo repetitivo y revisa lo que realmente importa.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Falla de comunicación
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Pero si técnicamente es obvio…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Falla:&lt;/strong&gt; olvidas que no todos hablan tu idioma.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solución:&lt;/strong&gt; traduce el lenguaje técnico al de negocio y viceversa. El contexto es poder.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Olvidar el factor humano
&lt;/h3&gt;

&lt;p&gt;Tu equipo no son tickets en Jira.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; haz &lt;em&gt;1:1&lt;/em&gt;, pregunta cómo están, protege su foco y reconoce sus logros.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Seguir queriendo ser el mejor programador
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; recalibra tu éxito.&lt;br&gt;&lt;br&gt;
Ya no eres el más rápido, eres quien hace que los demás lleguen más lejos.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 En resumen
&lt;/h2&gt;

&lt;p&gt;Pasar a &lt;strong&gt;Tech Lead&lt;/strong&gt; no es subir un peldaño, es &lt;strong&gt;cambiar de escalera&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Dejas de ser un &lt;em&gt;doer&lt;/em&gt; para convertirte en un &lt;em&gt;facilitador&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
Un &lt;strong&gt;multiplicador de fuerza&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si lo haces bien, tu impacto se multiplica.&lt;br&gt;&lt;br&gt;
Si no, acabarás quemado… y tu equipo también.&lt;/p&gt;




&lt;p&gt;📘 Estoy preparando una &lt;strong&gt;guía completa&lt;/strong&gt; sobre &lt;em&gt;“Cómo no fracasar en la transición de Senior Dev a Tech Lead”&lt;/em&gt;, con ejemplos y &lt;em&gt;prompts de IA&lt;/em&gt; para cada reto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://liderazgo.dominicode.com/?utm_source=dev_to" rel="noopener noreferrer"&gt;¿Te gustaría que la comparta en &lt;strong&gt;PDF gratis&lt;/strong&gt; cuando esté lista?&lt;/a&gt; &lt;/p&gt;

</description>
      <category>liderazgo</category>
      <category>tecnologia</category>
      <category>management</category>
    </item>
    <item>
      <title>Jest 30: Más Rápido, Inteligente y Listo para el Futuro. ¿Es Hora de Actualizar?</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Thu, 26 Jun 2025 05:51:57 +0000</pubDate>
      <link>https://dev.to/bezael/jest-30-mas-rapido-inteligente-y-listo-para-el-futuro-es-hora-de-actualizar-59ea</link>
      <guid>https://dev.to/bezael/jest-30-mas-rapido-inteligente-y-listo-para-el-futuro-es-hora-de-actualizar-59ea</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31vpsyyftm59eus44r0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31vpsyyftm59eus44r0p.png" alt="testing unit jest" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de una espera que pareció una eternidad en el vertiginoso mundo del desarrollo de software, el equipo de Jest ha lanzado la versión 30. Y permíteme decirte, la espera ha valido la pena. Esto no es una simple actualización incremental; es una reinvención fundamental que aborda deudas técnicas de larga data, responde a las críticas de la comunidad y reafirma a Jest como un titán en el panorama de las pruebas de JavaScript.&lt;/p&gt;

&lt;p&gt;Durante años, los desarrolladores hemos tenido una relación de amor-odio con Jest. Adoramos su simplicidad de "cero configuración" y su potente ecosistema, pero a menudo nos hemos enfrentado a tiempos de ejecución lentos y a un consumo de memoria descontrolado en proyectos grandes. Jest 30 no solo pule los bordes ásperos, sino que reconstruye los cimientos.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué esta actualización es un punto de inflexión?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adiós a la Lentitud
&lt;/h3&gt;

&lt;p&gt;El problema más notorio de Jest siempre ha sido el rendimiento. La versión 30 ataca este problema en dos frentes con una eficacia brutal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Módulos Ultrarrápidos:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
El principal cuello de botella era la forma en que Jest encontraba los módulos. En proyectos grandes, especialmente monorepos, la búsqueda recursiva de archivos &lt;code&gt;package.json&lt;/code&gt; consumía una cantidad desproporcionada de tiempo. Jest 30 reemplaza este sistema con &lt;code&gt;unrs-resolver&lt;/code&gt;, un nuevo componente de alto rendimiento que aprovecha la velocidad de Rust.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Resultados:&lt;/strong&gt; Los benchmarks oficiales muestran &lt;strong&gt;tiempos de ejecución hasta un 37% más rápidos&lt;/strong&gt; en aplicaciones TypeScript de gran tamaño.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Control de Fugas de Memoria:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
¿Quién no ha visto fallar su pipeline de CI por un misterioso error de "heap out of memory"? Jest 30 introduce una solución nativa con la opción &lt;code&gt;globalsCleanup&lt;/code&gt;. Esta característica detecta y limpia automáticamente las variables globales que se "filtran" entre pruebas, una de las principales causas de las fugas de memoria.&lt;br&gt;&lt;br&gt;
En el mismo benchmark, esta opción &lt;strong&gt;redujo el uso máximo de memoria en un increíble 77%&lt;/strong&gt; (de 7.8 GB a 1.8 GB).  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Esto no es una optimización, es un cambio radical que puede estabilizar por completo tus pruebas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Nuevas Herramientas para el Desarrollador
&lt;/h3&gt;

&lt;p&gt;Más allá de la velocidad, Jest 30 enriquece nuestra caja de herramientas con funcionalidades que simplifican patrones de prueba comunes y mejoran la legibilidad del código.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;expect.arrayOf(matcher)&lt;/code&gt;:&lt;/strong&gt;
Una de las adiciones más solicitadas. Ahora puedes verificar que cada elemento de un array cumple con un matcher específico de forma declarativa, sin necesidad de bucles manuales.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Verifica que cada objeto en el array tiene un 'id' de tipo string.&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectContaining&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;using spy&lt;/code&gt; para una Gestión de Mocks Impecable:&lt;/strong&gt;
Aprovechando la nueva sintaxis de "Explicit Resource Management" de JavaScript, ahora podemos crear espías que se restauran automáticamente al salir del ámbito.
¡Adiós a los olvidos de &lt;code&gt;mockRestore()&lt;/code&gt; en los bloques &lt;code&gt;afterEach&lt;/code&gt;!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;llama a console.warn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// El espía se restaura automáticamente al final del test. ¡Magia!&lt;/span&gt;
    &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;consoleWarnSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;un aviso&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consoleWarnSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reintentos de Pruebas Configurables:&lt;/strong&gt;
Las pruebas inestables (&lt;em&gt;flaky tests&lt;/em&gt;) son la pesadilla de la integración continua. &lt;code&gt;jest.retryTimes()&lt;/code&gt; ahora permite configurar un retardo entre reintentos (&lt;code&gt;waitBeforeRetry&lt;/code&gt;) o reintentar una prueba fallida de inmediato (&lt;code&gt;retryImmediately&lt;/code&gt;), dándote un control mucho más fino.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  La Migración: ¿Vale la Pena el Esfuerzo?
&lt;/h2&gt;

&lt;p&gt;Seamos claros: Jest 30 introduce cambios rompedores. Requiere &lt;strong&gt;Node.js 18+&lt;/strong&gt; y &lt;strong&gt;TypeScript 5.4+&lt;/strong&gt;, y elimina los alias de matchers obsoletos (como &lt;code&gt;toBeCalled&lt;/code&gt; en lugar de &lt;code&gt;toHaveBeenCalled&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Sin embargo, la migración es más sencilla de lo que parece. La mayoría de los cambios de alias se pueden solucionar automáticamente con &lt;code&gt;eslint-plugin-jest&lt;/code&gt;. El esfuerzo de actualizar es una inversión directa en una base de código de pruebas más moderna, robusta y, sobre todo, más rápida.&lt;/p&gt;




&lt;h2&gt;
  
  
  El Futuro de las Pruebas y Cómo Dominarlo
&lt;/h2&gt;

&lt;p&gt;Esta actualización demuestra que Jest no solo está vivo, sino que está evolucionando para liderar la próxima era de las pruebas en JavaScript. Escribir pruebas no es solo una formalidad; es un arte que, cuando se domina, garantiza la calidad y la mantenibilidad de nuestras aplicaciones.&lt;/p&gt;

&lt;p&gt;Si estas mejoras en Jest 30 te inspiran a llevar tus pruebas al siguiente nivel, especialmente en el ecosistema de Angular, he volcado años de experiencia en mi libro: &lt;a href="https://leanpub.com/testing-angular-jest" rel="noopener noreferrer"&gt;Unit testing con Jest y Testing Library en Angular&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Es la guía perfecta para dominar no solo las herramientas, sino las estrategias para escribir pruebas que realmente aporten valor y confianza a tu equipo.&lt;/p&gt;

&lt;p&gt;Las pruebas unitarias son la base de una pirámide de software sólida. Para una inmersión profunda en patrones prácticos, ejemplos del mundo real y cómo aprovechar al máximo herramientas como Jest y Testing Library, puedes encontrar más información y &lt;a href="https://www.angular-tests.dev/" rel="noopener noreferrer"&gt;adquirir el libro aquí&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión: Un Nuevo Comienzo para Jest
&lt;/h2&gt;

&lt;p&gt;Jest 30 es una declaración de intenciones. Es una respuesta contundente a las alternativas modernas y una promesa de un futuro con ciclos de lanzamiento más consistentes y un desarrollo más audaz. Las mejoras de rendimiento son reales y tangibles, las nuevas funcionalidades son ergonómicas y potentes, y la hoja de ruta es clara.&lt;/p&gt;

&lt;p&gt;Si has estado luchando con la lentitud de Jest o te has sentido tentado por otras herramientas, esta es la actualización que estabas esperando. Es el momento de actualizar, explorar las nuevas funcionalidades y llevar tus pruebas a un nuevo nivel de eficiencia y fiabilidad. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El futuro de las pruebas con Jest es brillante.&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>jest</category>
      <category>testing</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>De Jasmine a Jest: Migración sin dolor en 30 minutos</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Sun, 15 Jun 2025 19:36:36 +0000</pubDate>
      <link>https://dev.to/bezael/de-jasmine-a-jest-migracion-sin-dolor-en-30-minutos-2onm</link>
      <guid>https://dev.to/bezael/de-jasmine-a-jest-migracion-sin-dolor-en-30-minutos-2onm</guid>
      <description>&lt;p&gt;¿Te imaginas recortar a la mitad el tiempo que tardan tus pruebas de Angular con un simple giro de tuercas que casi nadie está aplicando todavía?&lt;br&gt;
Hoy vas a descubrir cómo hacerlo y por qué aplazarlo podría costarte horas de café frío y deploys retrasados.&lt;/p&gt;
&lt;h2&gt;
  
  
  La verdad incómoda sobre Karma
&lt;/h2&gt;

&lt;p&gt;Karma ha sido el “motor” oficial durante años.&lt;br&gt;
Pero en Angular 16 ya apareció en la lista de obsoletos y, desde la versión 17, vive con fecha de caducidad impresa.&lt;br&gt;
Cada lanzamiento de navegador, cada reconexión y cada pestaña oculta que se abre en segundo plano es un ladrillo más en la pared que separa tu código de la luz verde del &lt;em&gt;pipeline&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Por qué Jest está ganando terreno en silencio
&lt;/h3&gt;

&lt;p&gt;Jest no necesita un navegador real para ejecutar tests: todo sucede en memoria, como quien hace cálculos sobre la servilleta del bar.&lt;br&gt;
Los informes son legibles, coloreados y te señalan el error con una flecha casi insultante.&lt;br&gt;
Además, el caché inteligente evita repetir trabajo y acelera aún más cada iteración.&lt;br&gt;
La comunidad ya lo adoptó; el propio equipo de Angular lo reconoce como la opción a futuro. ¿Vas a quedarte fuera?&lt;/p&gt;
&lt;h3&gt;
  
  
  Tu plan exprés de 30 minutos
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actualiza dependencias&lt;/strong&gt;&lt;br&gt;
Añade el &lt;em&gt;preset&lt;/em&gt; oficial de Angular para Jest y retira los paquetes de Karma. Con una sola orden de instalación limpias el terreno.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prepara la configuración&lt;/strong&gt;&lt;br&gt;
Reemplaza el archivo de Karma por el de Jest. Solo necesitas declarar el &lt;em&gt;preset&lt;/em&gt;, el directorio de cobertura y los mapeos de &lt;em&gt;paths&lt;/em&gt; que ya usas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adapta los &lt;em&gt;setup files&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Si inicializabas zonas o &lt;em&gt;polyfills&lt;/em&gt; especiales, muévelos al archivo de arranque de Jest. El formato cambia, pero el contenido es prácticamente el mismo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ajusta los &lt;em&gt;test utilities&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;TestBed&lt;/code&gt; sigue funcionando; la diferencia es que las utilidades de Jest —&lt;em&gt;spies&lt;/em&gt;, temporizadores falsos, &lt;em&gt;snapshots&lt;/em&gt;— sustituyen a las de Jasmine sin dolor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Corre la primera pasada&lt;/strong&gt;&lt;br&gt;
Ejecuta tus pruebas. Lo habitual es ver una ráfaga verde y un tiempo de ejecución que parece un error de imprenta: tan rápido que querrás presumir la captura.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Lo que nadie te cuenta (pero necesitas saber)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cobertura instantánea&lt;/strong&gt;: Jest muestra el porcentaje de líneas cubiertas sin plugins extra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migración incremental&lt;/strong&gt;: puedes mantener algunos archivos en Jasmine mientras terminas la transición.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración continua simplificada&lt;/strong&gt;: adiós a los &lt;em&gt;launchers&lt;/em&gt; de navegador y a esa configuración críptica que nunca recuerdas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ¿Qué pasa si lo dejas para mañana?
&lt;/h3&gt;

&lt;p&gt;Karma desaparecerá del &lt;em&gt;CLI&lt;/em&gt; y tendrás que migrar deprisa, bajo presión y con todo el equipo mirando el reloj.&lt;br&gt;
El coste no será solo técnico: cada minuto de espera en cada &lt;em&gt;build&lt;/em&gt; se multiplicará por cada desarrollador y cada &lt;em&gt;pull request&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Da el salto ahora mismo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://angular-tests.dev" rel="noopener noreferrer"&gt;Haz clic aquí y descarga el ebook&lt;/a&gt; &lt;strong&gt;“Unit testing con Jest y Testing Library en Angular”&lt;/strong&gt;.&lt;br&gt;
Y tendrás tu proyecto corriendo con Jest y disfrutarás de test suites que vuelan.&lt;/p&gt;
&lt;h3&gt;
  
  
  Recuerda el beneficio final
&lt;/h3&gt;

&lt;p&gt;Migrar hoy significa ciclos de feedback rapidísimos, menos frustración y más tiempo para crear funcionalidades que importan.&lt;br&gt;
No dejes que un &lt;em&gt;runner&lt;/em&gt; obsoleto decida tu velocidad de desarrollo: adopta Jest y comprueba cómo tus pruebas dejan de ser un lastre para convertirse en tu mejor aliado.&lt;/p&gt;
&lt;h4&gt;
  
  
  Paso 1 – Instala lo esencial sin perder un minuto
&lt;/h4&gt;

&lt;p&gt;Abre tu terminal favorita y ejecuta el gestor de paquetes para añadir &lt;strong&gt;Jest&lt;/strong&gt;, &lt;strong&gt;jest-preset-angular&lt;/strong&gt; y &lt;strong&gt;@types/jest&lt;/strong&gt; como dependencias de desarrollo.&lt;br&gt;
Con esta sola acción dejas listo el motor, el adaptador específico para Angular y las definiciones de tipos que evitarán errores de compilación más adelante.&lt;/p&gt;

&lt;p&gt;Confirma al terminar que el archivo de bloqueos (ya sea &lt;code&gt;package-lock.json&lt;/code&gt; o &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;) refleje las tres librerías; eso te garantiza entornos reproducibles en todo el equipo.&lt;/p&gt;

&lt;p&gt;¿Listo? En menos de lo que tarda en hervir el café, ya tienes las bases instaladas y puedes pasar al siguiente ajuste de configuración.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest jest-preset-angular @types/jest
&lt;span class="c"&gt;# o si usas yarn&lt;/span&gt;
yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; jest jest-preset-angular @types/jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Ajuste del &lt;code&gt;package.json&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Imagina que el corazón de tu proyecto late en el &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
El próximo latido ­–el que activará Jest– se cocina con un simple ajuste.&lt;/p&gt;

&lt;p&gt;Abre ese archivo y localiza el bloque &lt;strong&gt;scripts&lt;/strong&gt;.&lt;br&gt;
Dentro verás la orden clásica que dispara Karma; bórrala sin piedad.&lt;/p&gt;

&lt;p&gt;En su lugar, coloca una instrucción que invoque &lt;strong&gt;Jest&lt;/strong&gt; de forma directa, sin corredores intermedios ni arranques de navegador.&lt;/p&gt;

&lt;p&gt;Con esta única sustitución, cada vez que escribas &lt;code&gt;npm run test&lt;/code&gt; (o el alias que uses) entrarás en la nueva era de tests turboalimentados.&lt;/p&gt;

&lt;p&gt;Guarda el cambio, ejecuta la prueba… y comprueba cómo el cronómetro se hunde: tu suite ahora corre a la velocidad que siempre quisiste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx jest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Configuración de &lt;code&gt;tsconfig.spec.json&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;¿Todavía ves el fantasma de &lt;strong&gt;Jasmine&lt;/strong&gt; acechando en tu &lt;code&gt;tsconfig.spec.json&lt;/code&gt;?&lt;br&gt;
Ese pequeño detalle frena a TypeScript y puede hacer que tus &lt;em&gt;imports&lt;/em&gt; de Jest luzcan como errores.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Localiza la puerta de entrada&lt;/strong&gt;&lt;br&gt;
Abre &lt;code&gt;tsconfig.spec.json&lt;/code&gt; y busca la propiedad &lt;code&gt;types&lt;/code&gt; dentro de &lt;code&gt;compilerOptions&lt;/code&gt;. Allí es donde “vive” la referencia a los tipos globales que entiende el compilador.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expulsa al huésped equivocado&lt;/strong&gt;&lt;br&gt;
Si la lista contiene &lt;code&gt;"jasmine"&lt;/code&gt;, sustitúyelo por &lt;code&gt;"jest"&lt;/code&gt;. Puedes mantener otras entradas útiles (por ejemplo, &lt;code&gt;"node"&lt;/code&gt;) si ya estaban presentes: la regla es dejar solo el conjunto de tipos que realmente necesitas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Guarda y respira&lt;/strong&gt;&lt;br&gt;
A partir de este cambio, TypeScript reconocerá todas las funciones globales de Jest (&lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt;, &lt;code&gt;expect&lt;/code&gt;, etc.) sin que necesites &lt;em&gt;imports&lt;/em&gt; extra ni comentarios de bypass. Un simple &lt;code&gt;npm run test&lt;/code&gt; confirmará que las advertencias desaparecieron y que tu nueva configuración late con normalidad.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con esa edición mínima habrás desalojado por completo los viejos tipos de Jasmine y tu proyecto quedará afinado para el nuevo corredor de pruebas.&lt;/p&gt;

&lt;p&gt;¿Listo para el siguiente ajuste o hay algo que quieras pulir antes de continuar?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out-tsc/spec"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cambia&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jasmine"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;por&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.spec.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.d.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Creación de &lt;code&gt;jest.config.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;¿Preparado para darle a Jest las llaves de tu proyecto?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Crea el archivo&lt;/strong&gt;&lt;br&gt;
En la raíz, añade un nuevo &lt;code&gt;jest.config.ts&lt;/code&gt;. Piensa en él como la consola central desde la que controlarás cada test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importa el preset oficial&lt;/strong&gt;&lt;br&gt;
El primer paso dentro del archivo es “enchufar” &lt;code&gt;jest-preset-angular&lt;/code&gt;. Con eso, Jest entenderá componentes, pipes y templates como si llevara años hablando Angular.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define la configuración&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;preset&lt;/strong&gt;: apunta a &lt;code&gt;'jest-preset-angular'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;testEnvironment&lt;/strong&gt;: usa &lt;code&gt;'jsdom'&lt;/code&gt; para una simulación ligera de navegador.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setupFilesAfterEnv&lt;/strong&gt;: referencia a un pequeño &lt;code&gt;jest.setup.ts&lt;/code&gt; donde inicialices &lt;code&gt;jest-preset-angular&lt;/code&gt;, fakeAsync zones o utilidades globales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;transform&lt;/strong&gt;: indica a Jest que los archivos &lt;code&gt;.ts&lt;/code&gt; y &lt;code&gt;.html&lt;/code&gt; pasen por el transformador que trae el preset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;moduleNameMapper&lt;/strong&gt;: replica aquí los alias de paths que ya tienes en tu &lt;code&gt;tsconfig&lt;/code&gt;, de modo que los imports sigan funcionando.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;coverageDirectory&lt;/strong&gt;: elige la carpeta donde quieras el informe de cobertura; así mantienes los resultados fuera de la carpeta de artefactos principales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;transformIgnorePatterns&lt;/strong&gt;: excluye el código de terceros (por ejemplo, &lt;code&gt;node_modules/(?!(lodash-es|rxjs))&lt;/code&gt;) si necesitas transpilar alguna librería moderna.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guarda y prueba&lt;/strong&gt;
Con solo ese puñado de líneas, Jest sabe exactamente cómo arrancar tus specs de Angular. Lanza &lt;code&gt;npm run test&lt;/code&gt; y observa cómo se disparan los resultados: rápido, limpio y con un informe de cobertura listo para presumir.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sin tocar una sola línea de tu código de producción, habrás levantado la nueva central de mando para tus pruebas unitarias. Tu stack ya funciona con Jest; lo siguiente es disfrutar de ciclos de feedback que casi no dan tiempo ni a parpadear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest-preset-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;setupFilesAfterEnv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/setup-jest.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;moduleDirectories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/+(*.)+(spec).+(ts)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Creación de &lt;code&gt;setup-jest.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;¿Sabías que un único archivo puede convocar todo el poder de Jest y al mismo tiempo convencer a Angular de que nada ha cambiado? Ese archivo se llama &lt;code&gt;setup-jest.ts&lt;/code&gt;, y su misión es dejar listo el escenario antes de que se ejecute la primera prueba.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por qué importa
&lt;/h3&gt;

&lt;p&gt;Jest necesita que le cuentes cómo funcionan las zonas de Angular, cómo compilar plantillas y en qué momento «pinchar» el &lt;em&gt;TestBed&lt;/em&gt;. Sin esa preparación, tus &lt;em&gt;components&lt;/em&gt; quedarían mudos y tus &lt;em&gt;pipes&lt;/em&gt; perderían la voz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que debe ocurrir dentro
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encendido del preset&lt;/strong&gt;&lt;br&gt;
El archivo arranca cargando la utilería que traduce internamente los decoradores, las plantillas y las dependencias que Angular usa de forma nativa.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parches y &lt;em&gt;polyfills&lt;/em&gt; opcionales&lt;/strong&gt;&lt;br&gt;
Si tu app depende de APIs del navegador que JSDOM no expone por defecto—como &lt;code&gt;matchMedia&lt;/code&gt; o ciertas animaciones—, aquí añades los parches mínimos para que Jest no tropiece.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensiones a las expectativas&lt;/strong&gt;&lt;br&gt;
Puedes incorporar ayudas de aserción adicionales—por ejemplo, comparadores de fechas o números aproximados—sin repetir imports en cada archivo de prueba.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuración global de tiempo&lt;/strong&gt;&lt;br&gt;
Ajustar un &lt;em&gt;timeout&lt;/em&gt; genérico evita que las pruebas se queden colgadas por operaciones lentas, manteniendo el reporte limpio y controlado.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con esa coreografía, cada spec encuentra un Angular completamente operativo en milésimas de segundo, sin demoras ni advertencias extrañas. Guarda el archivo junto al resto de tu código y lánzate a ejecutar &lt;code&gt;npm run test&lt;/code&gt;: verás cómo todo fluye con la seguridad de saber que tu entorno está perfectamente preparado antes de que el cronómetro arranque.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest-preset-angular/setup-jest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/localize/init&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Necesario para Angular &amp;gt;= 9&lt;/span&gt;
&lt;span class="c1"&gt;// Workaround para ReferenceError: TextEncoder is not defined en Jest&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TextEncoder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TextEncoder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Deja atrás el lastre de Karma y Jasmine
&lt;/h3&gt;

&lt;p&gt;Ahora que tu suite vuela con Jest, mantener las viejas dependencias es como llevar un ancla colgada al tobillo. Deshazte de &lt;strong&gt;Karma&lt;/strong&gt;, &lt;strong&gt;Jasmine&lt;/strong&gt; y cualquier &lt;em&gt;launcher&lt;/em&gt; o &lt;em&gt;reporter&lt;/em&gt; relacionado: liberarás espacio, acelerarás las instalaciones de CI y evitarás conflictos de versiones en el futuro.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Depuración del &lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Despídete de cada librería que empiece por &lt;em&gt;karma-&lt;/em&gt; o &lt;em&gt;jasmine-&lt;/em&gt;. Al retirarlas, tu árbol de dependencias se encoge y las descargas en cada &lt;em&gt;pipeline&lt;/em&gt; se reducen a la mitad.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limpieza de archivos de configuración&lt;/strong&gt;&lt;br&gt;
Borra &lt;code&gt;karma.conf.js&lt;/code&gt;, &lt;code&gt;test.ts&lt;/code&gt; (si lo tienes) y cualquier script de &lt;em&gt;npm&lt;/em&gt; que invoque Karma. Si tu CI aún abre un navegador para los tests, elimínalo también: ese paso dejó de tener sentido.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Revisión de scripts y &lt;em&gt;tasks&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Comprueba que no queden comandos heredados en la carpeta de herramientas, ni pasos de build que referencien al viejo runner. Una búsqueda rápida por el repositorio te dará la certeza.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actualización del &lt;em&gt;cache&lt;/em&gt; de CI&lt;/strong&gt;&lt;br&gt;
Sin Karma ni Chrome Headless en la ecuación, tu contenedor construirá menos capas y completará los tests antes de que acabes el primer sorbo de café.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirmación final&lt;/strong&gt;&lt;br&gt;
Ejecuta la suite con Jest en local y en la pipeline. Verás cómo la lista de paquetes descargados se acorta y los tiempos de ejecución marcan nuevos mínimos históricos.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con esta purga, tu proyecto queda más ligero, tu presupuesto de CI respira y tu equipo trabaja sobre un stack coherente y enfocado en el futuro. Sigue avanzando; cada archivo antiguo que eliminas es un segundo menos de espera en tu próximo &lt;em&gt;deploy&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall karma karma-chrome-launcher karma-jasmine karma-coverage karma-jasmine-html-reporter @types/jasmine @types/jasminewd2 jasmine-core jasmine-spec-reporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ejemplo mínimo: Conversión de una prueba tradicional a Jest con Testing Library
&lt;/h3&gt;

&lt;p&gt;Veamos un ejemplo sencillo de cómo se vería una prueba antes y después de la migración.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Componente &lt;code&gt;CounterComponent&lt;/code&gt; (ejemplo):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// counter.component.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;button (click)="decrement()"&amp;gt;-&amp;lt;/button&amp;gt;
    &amp;lt;span data-testid="count"&amp;gt;{{ count() }}&amp;lt;/span&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;+&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prueba tradicional (Karma/Jasmine con &lt;code&gt;TestBed&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// counter.component.spec.ts (Antes)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./counter.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CounterComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;compileComponents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Haves an initial count of 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Renders the initial count of 0 in the span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="count"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Increment the count signal when increment() is called&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Decrement the count signal when decrement() is called&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Increment the count in the template when the "+" button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="count"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;incrementButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Decrement the count in the template when the "-" button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="count"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;decrementButton&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prueba migrada (Jest):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./counter.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CounterComponent (con Jest) - Versión Mejorada&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCountSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="count"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;compileComponents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Initial State and Rendering&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Initialize with a count of 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Renders the initial count "0" inside the span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCountSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Renders an increment and a decrement button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;incrementButton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decrementButton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Business Logic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Update the count signal from 0 to 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Update the count signal from 0 to -1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Handles multiple operations correctly (e.g., increment twice, decrement once)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User Interaction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Update the displayed count to "1" when the "+" button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;incrementButton&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCountSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Update the displayed count to "-1" when the "-" button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;decrementButton&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCountSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Displays "0" after clicking "+" and then "-"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decrementButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;incrementButton&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;decrementButton&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCountSpan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Lo mejor de todo? Ahora tu archivo de pruebas no solo “pasa”, sino que respira mantenimiento futuro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por qué tus ajustes son oro puro
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Selectores resilientes&lt;/strong&gt;&lt;br&gt;
Buscar por texto (+ / −) anula la fragilidad del índice numérico. Mañana podrás insertar un botón “reset” en cualquier parte y el test ni se despeinará.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Describe anidados que cuentan una historia&lt;/strong&gt;&lt;br&gt;
Al separar Estado Inicial, Lógica y Interacción, cualquiera localiza la intención en segundos. Leer el archivo deja de ser un safari de &lt;code&gt;it()&lt;/code&gt; sueltos y pasa a ser un mapa bien señalizado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Helpers DRY&lt;/strong&gt;&lt;br&gt;
Centralizar la obtención de elementos reduce el “ruido” en cada spec y concentra el cambio en un único punto. Si algún día migras de &lt;code&gt;data-testid&lt;/code&gt; a atributos ARIA, actualizarás solo esas funciones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cobertura real de flujos&lt;/strong&gt;&lt;br&gt;
Verificar la presencia de ambos botones y la secuencia + → − garantiza que tu contador no falla ante combinaciones encadenadas. Es un test de comportamiento, no una foto estática.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cómo exprimir aún más tu nueva base
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Añade tests de accesibilidad&lt;/strong&gt;&lt;br&gt;
Usa &lt;code&gt;jest-axe&lt;/code&gt; para asegurarte de que los cambios visuales no rompen reglas WCAG. Mantendrás al equipo en verde respecto a inclusión sin esfuerzo manual.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integra &lt;em&gt;snapshots&lt;/em&gt; selectivos&lt;/strong&gt;&lt;br&gt;
Útiles para componentes con mucho markup. Hazlo solo en piezas poco volátiles; así evitarás falsas alarmas en cada refactor visual menor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mide la cobertura de ramas&lt;/strong&gt;&lt;br&gt;
Activa &lt;code&gt;coverageReporters: ['text', 'lcov']&lt;/code&gt; y revisa los puntos ciegos. Verás con claridad qué caminos lógicos no pisan todavía tus pruebas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimiza los fake timers&lt;/strong&gt;&lt;br&gt;
Para lógica basada en &lt;code&gt;setTimeout&lt;/code&gt; o &lt;code&gt;debounce&lt;/code&gt;, los temporizadores controlados de Jest aceleran las specs y evitan esperas innecesarias.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Revisa tu pipeline de CI&lt;/strong&gt;&lt;br&gt;
Ahora que Karma salió de escena, ajusta el caché de dependencias y elimina pasos de navegador. Ganas minutos en cada ejecución automática.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Has sentado un cimiento robusto: pruebas legibles, resistentes y fáciles de ampliar. A partir de aquí, cualquier nueva funcionalidad se incorporará con confianza y sin temer al efecto dominó.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: Jest y Angular Testing Library
&lt;/h3&gt;

&lt;p&gt;¿Listo para ver cómo el mismo test respira libertad sin &lt;code&gt;fixture&lt;/code&gt;, sin &lt;code&gt;detectChanges()&lt;/code&gt; y sin ese ruido que encoge la vista?&lt;/p&gt;

&lt;p&gt;Observa esta versión con &lt;a href="https://bit.ly/testing-angular-jest" rel="noopener noreferrer"&gt;&lt;strong&gt;Angular Testing Library&lt;/strong&gt;&lt;/a&gt;: minimalista, robusta y pensada para el futuro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prueba migrada (Jest y Angular Testing Library):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// counter.component.spec.ts (Después)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./counter.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/jest-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Para matchers extendidos como.toBeInTheDocument()&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CounterComponent (Jest/Testing Library)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Helper centralizado: un solo punto de cambio&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// crea componente + fixture por ti&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create and display initial count of 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Increment count when + button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Decrement count when - button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sin fixtures ni &lt;code&gt;TestBed.compileComponents()&lt;/code&gt;&lt;/strong&gt;. &lt;code&gt;render()&lt;/code&gt; hace la magia tras bambalinas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selectores accesibles (&lt;code&gt;getByRole&lt;/code&gt;)&lt;/strong&gt;: resistentes a cambios de orden o estilo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flujos síncronos por defecto&lt;/strong&gt;: cuando algo sea realmente asíncrono, usa &lt;code&gt;await waitFor(() =&amp;gt; …)&lt;/code&gt; y listo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Te gustó la sensación de limpieza? En mi &lt;a href="https://angular-tests.dev" rel="noopener noreferrer"&gt;libro sobre testing moderno en Angular&lt;/a&gt;, profundizo en &lt;em&gt;querying&lt;/em&gt; accesible, patrones de &lt;em&gt;state observers&lt;/em&gt; y prácticas de rendimiento que hacen que tus tests vuelen.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Prefieres aprender mirando? Aquí tienes tu atajo visual
&lt;/h3&gt;

&lt;p&gt;Sé que la instalación de &lt;strong&gt;Angular Testing Library&lt;/strong&gt; merece un tutorial aparte; lo verás detallado en el próximo artículo.&lt;/p&gt;

&lt;p&gt;Mientras tanto, si eres de los que absorben conceptos viendo cada clic en tiempo real, he preparado un vídeo exprés donde compruebas, paso a paso, cómo escribir un &lt;strong&gt;unit test de un &lt;code&gt;input required&lt;/code&gt; con Jest&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;▶️ *&lt;em&gt;Ver el vídeo en YouTube *&lt;/em&gt;  &lt;iframe src="https://www.youtube.com/embed/JMU_2dgzueI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Beneficios medibles: Tiempo de ejecución y reporte de cobertura
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;En pocas líneas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Velocidad a otro nivel&lt;/strong&gt;&lt;br&gt;
Jest ejecuta tests en paralelo y en JSDOM; suites que tardaban 15 min con Karma pueden bajar a 1-2 min.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cobertura con un flag&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;jest --coverage&lt;/code&gt; genera informes detallados sin plugins extra, así ves al instante qué ramas faltan por probar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mocking integrado y simple&lt;/strong&gt;&lt;br&gt;
Con &lt;code&gt;jest.fn()&lt;/code&gt; o &lt;code&gt;jest.mock()&lt;/code&gt; aísla servicios y APIs globales sin configurar librerías adicionales.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Snapshot testing&lt;/strong&gt;&lt;br&gt;
Captura la salida del componente y detecta al momento cualquier cambio inesperado en la UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En conjunto, obtienes ciclos de feedback mucho más rápidos, builds de CI más baratas y una confianza mayor en cada refactor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusión
&lt;/h3&gt;

&lt;p&gt;Dar el salto de Karma/Jasmine a Jest es pasar de un motor con años de servicio a uno pensado para la velocidad y la claridad de hoy. En minutos tendrás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pruebas que se ejecutan en paralelo y terminan antes de que apures el café.&lt;/li&gt;
&lt;li&gt;Cobertura de código con un simple &lt;code&gt;--coverage&lt;/code&gt;, sin plugins ni rodeos.&lt;/li&gt;
&lt;li&gt;Mocks y snapshots listos para blindar cada refactor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada segundo ganado en el test runner vuelve a tu desarrollo: menos espera, más funciones nuevas, mayor confianza.&lt;/p&gt;

&lt;p&gt;¿Quieres ver cómo aplicar Jest a casos reales y tener ejemplos de producción listos para copiar y pegar? Descúbrelo en nuestro eBook y lleva tus pruebas al siguiente nivel. &lt;a href="https://bit.ly/testing-angular-jest" rel="noopener noreferrer"&gt;Haz clic aquí y conviértete&lt;/a&gt; en el desarrollador que rompe la barra de progreso antes de que empiece a cargar.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>jest</category>
      <category>testing</category>
    </item>
    <item>
      <title>Cómo Crear un Framework de JavaScript desde Cero - La Historia de Brisa Build</title>
      <dc:creator>Bezael Pérez</dc:creator>
      <pubDate>Mon, 28 Oct 2024 22:50:00 +0000</pubDate>
      <link>https://dev.to/bezael/como-crear-un-framework-de-javascript-desde-cero-la-historia-de-brisa-build-28im</link>
      <guid>https://dev.to/bezael/como-crear-un-framework-de-javascript-desde-cero-la-historia-de-brisa-build-28im</guid>
      <description>&lt;p&gt;En el mundo de desarrollo web, los frameworks de JavaScript se han vuelto herramientas esenciales para la creación de aplicaciones modernas. Desde Angular y React hasta Vue y Svelte, cada uno ofrece un enfoque único para resolver problemas de arquitectura, rendimiento y experiencia de usuario. Sin embargo, muchos desarrolladores se han preguntado: ¿realmente necesitamos otro framework de JavaScript?&lt;/p&gt;

&lt;p&gt;Hoy quiero compartir la historia de cómo y por qué se creó Brisa, un nuevo framework de JavaScript, de la mano de su propio creador, Aral Roca. &lt;strong&gt;Brisa&lt;/strong&gt; no solo busca resolver problemas específicos de performance y modularidad en aplicaciones web, sino que también plantea una visión innovadora sobre cómo los desarrolladores pueden construir interfaces modernas sin perder de vista el funcionamiento interno de la web y el servidor.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/WOTKdwxM93Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;¿Qué es Brisa?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Brisa es un framework que ha sido desarrollado desde cero con el propósito de mejorar la comunicación entre el cliente y el servidor, aprovechando al máximo la tecnología de web components y signals. Uno de los principales problemas que Aral identificó en muchos frameworks actuales era la complejidad de manejar acciones del servidor y del cliente de forma eficiente. Con Brisa, cualquier evento puede ejecutarse en el servidor, y el código que se necesita en el cliente es mínimo.&lt;/p&gt;

&lt;p&gt;Un aspecto innovador de Brisa es que funciona sin necesidad de escribir código en el cliente para manejar eventos en el servidor. A través de un sistema llamado RPC (Remote Procedure Call) de solo 2 KB, Brisa permite que las interacciones se procesen directamente desde el servidor. Esto no solo facilita el desarrollo, sino que también reduce significativamente la cantidad de código necesario en el cliente, mejorando así el rendimiento general de la aplicación.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;El Origen de Brisa&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;La idea de crear Brisa nació cuando Aral tuvo una conversación inspiradora con Misko Hevery, el creador de Quick.js, durante un evento en Barcelona. La visión de Misko y su enfoque para mejorar la performance en aplicaciones web dejaron una marca en Aral. &lt;br&gt;
"Fue como si estuviera hablando de su propio hijo", dice Aral, recordando la conversación. Inspirado por este nivel de pasión y compromiso, Aral decidió que él también quería crear un framework, pero no uno más en el montón; quería algo que resolviera problemas que él mismo había enfrentado.&lt;/p&gt;

&lt;p&gt;Así, comenzó a trabajar en Brisa, un proyecto que combina lo mejor de las interacciones en tiempo real entre cliente y servidor, y que es lo suficientemente ligero como para integrarse sin fricciones con otros componentes web. Brisa surgió como un experimento que luego se convirtió en un framework completo, diseñado para la eficiencia y la escalabilidad en la web moderna.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Principales Características de Brisa&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Server Actions sin Us Client y Us Server: En Brisa, todo el código se ejecuta en el servidor por defecto. Solo en situaciones específicas se habilita la ejecución en el cliente, lo cual simplifica la gestión del código y evita complejidades innecesarias.&lt;/p&gt;

&lt;p&gt;Components Web Integrados: Los web components de Brisa son completamente modulares y se pueden utilizar en otros frameworks. Esto significa que cualquier componente creado en Brisa se puede reutilizar en React, Angular, o Vue, brindando a los desarrolladores mayor flexibilidad.&lt;/p&gt;

&lt;p&gt;Signals Avanzados: Brisa implementa un sistema de signals para la gestión de estados. Estos signals permiten que los componentes se actualicen automáticamente cuando cambian sus propiedades, sin necesidad de un render completo de la página. Esto optimiza el rendimiento y reduce la carga del cliente.&lt;/p&gt;

&lt;p&gt;RPC de Solo 2 KB: Uno de los aspectos más innovadores de Brisa es su sistema de RPC de solo 2 KB, que permite a los desarrolladores manejar interacciones desde el servidor sin necesidad de escribir código en el cliente. Esto resulta en un código mucho más ligero y simplificado, mejorando la eficiencia general de las aplicaciones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz16b8w84z3rlvxdrihxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz16b8w84z3rlvxdrihxf.png" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;¿Realmente Necesitamos Otro Framework?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Es comprensible que algunos desarrolladores se pregunten si realmente necesitamos otro framework de JavaScript. La respuesta de Aral Roca es clara: "Con cada nuevo framework vienen nuevas oportunidades de mejorar y de aprender". &lt;/p&gt;

&lt;p&gt;A lo largo de los años, hemos visto cómo cada framework ha aportado algo diferente y único. React nos enseñó sobre el virtual DOM, Angular sobre la modularidad en gran escala, y Vue sobre la simplicidad en el desarrollo de componentes. Brisa, por su parte, nos muestra cómo combinar la potencia de la plataforma web y las técnicas de renderizado desde el servidor para optimizar aplicaciones modernas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Aplicaciones de Brisa en el Mundo Real&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Las ventajas de Brisa son particularmente valiosas en aplicaciones de alto rendimiento, como aquellas que requieren de cargas rápidas y mínima latencia en el manejo de datos en tiempo real. &lt;strong&gt;Brisa permite que la&lt;/strong&gt; comunicación entre el cliente y el servidor sea mucho más eficiente, lo cual resulta ideal para aplicaciones que manejan un gran volumen de interacciones de usuario. &lt;br&gt;
Además, su integración con los web components facilita la creación de aplicaciones modulares que pueden integrarse con otras tecnologías sin problemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;La Comunidad Detrás de Brisa&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Aral también enfatiza la importancia de la comunidad en el desarrollo de Brisa. Para él, el apoyo y las contribuciones de otros desarrolladores han sido fundamentales. “La mejor parte de trabajar en Brisa ha sido la oportunidad de aprender de otros desarrolladores y ver cómo ellos aplican este framework en sus propios proyectos”, comenta Aral. Brisa es un framework abierto y siempre se anima a la comunidad a contribuir, ya sea con ideas, reporte de problemas, o nuevas funcionalidades.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;¿Qué Sigue para Brisa?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Aral tiene grandes planes para el futuro de Brisa. Con la posibilidad de que los signals sean incluidos en la web platform oficial gracias a TC39, el comité encargado de la &lt;strong&gt;evolución de JavaScript&lt;/strong&gt;, Brisa está en una excelente posición para volverse aún más eficiente y ligero. Si se incluye la funcionalidad de signals en el lenguaje base, las aplicaciones que usen Brisa podrían beneficiarse de un rendimiento aún mayor, con menos dependencia de bibliotecas externas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;¿Quieres Probar Brisa?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Si eres un desarrollador curioso o te interesa conocer más sobre la &lt;strong&gt;arquitectura de aplicaciones web avanzadas&lt;/strong&gt;, te invito a darle una oportunidad a Brisa. No solo verás un enfoque innovador para resolver problemas de rendimiento y modularidad, sino que también podrás aprender sobre las últimas tendencias en el desarrollo de JavaScript.&lt;/p&gt;

&lt;p&gt;¡Comparte tus pensamientos en los comentarios! &lt;br&gt;
¿Te entusiasma la idea de un nuevo framework como Brisa? &lt;br&gt;
¿Qué piensas sobre el futuro de los frameworks de JavaScript? &lt;/p&gt;

&lt;p&gt;¡Espero tus opiniones!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>brisabuild</category>
      <category>programador</category>
    </item>
  </channel>
</rss>
