El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Guía de pruebas unitarias de Cursor AI: haz esto si quieres evitar dolores de cabeza.

i se produce un error, es estupendo, ya que Cursor tiene el contexto exacto del caso de prueba sobre el error y, si lo combinas con la descripción general de tu proyecto, verás que empieza a corregir los errores por sí solo.

· 8 min de lectura
Guía de pruebas unitarias de Cursor AI: haz esto si quieres evitar dolores de cabeza.

Me he metido de lleno en un trabajo más intenso con Cursor que ha requerido muchas partes complejas.

Como resultado, las pruebas se han vuelto más críticas y pensé en escribir una entrada para mostraros cómo podéis automatizar vuestras pruebas con Cursor.

Esta entrada del blog os será útil si:

  • Queréis crear proyectos más complicados con Cursor.
  • Queréis reducir las solicitudes y las comprobaciones de errores y que el LLM se encargue de ello.
  • Queréis aprender sobre las pruebas automatizadas.
  • Os da dolor de cabeza que la IA lo estropee todo aleatoriamente.

Vamos a utilizar un proyecto financiero como caso de prueba. Para empezar, vamos a mostrar un ejemplo de cómo conseguir que el sistema ayude a automatizar las pruebas de nuestro proceso de creación de facturas.

He creado un sencillo sistema de facturación para estas pruebas.

Configuración de nuestras pruebas

En primer lugar, definamos qué queremos que el sistema pruebe automáticamente:

  • El usuario puede navegar a la página de facturas
  • Envío de formularios cuando crean una factura
  • Actualizaciones de estado y órdenes de compra

Para ello, utilizaremos una herramienta llamada Playwright

Me encanta Playwright, ya que ofrece un panel de control muy útil con todas nuestras pruebas y su estado.

Playwright es genial, ya que nos ofrece una página de panel de control clara y agradable con todas nuestras pruebas y sus estados.

¿Cómo es una prueba?

La prueba es solo un pequeño archivo de código que Cursor puede escribir por ti.

test("User can navigate to invoices page", async ({ page }) => {
    // Start from the homepage
    await page.goto("/");

    // Should be redirected to dashboard
    await expect(page).toHaveURL("/dashboard");

    // Navigate to invoices
    await page.click("text=Invoices");
    await expect(page).toHaveURL("/dashboard/invoices");

    // Verify the page has loaded correctly
    await expect(page.locator("h1")).toContainText("Invoices");
    await expect(page.locator("a:has-text('New Invoice')")).toBeVisible();
});

El archivo anterior:

  • Navega desde la página de inicio al panel de control y, a continuación, va a la página de facturas.
  • A continuación, comprueba que la página de facturas se ha cargado correctamente.

Una prueba más complicada con un formulario, que muestra una prueba que impide el envío de datos no válidos.

test("Form validation prevents submission of invalid data", async ({ page }) => {
    // Go to new invoice page
    await page.goto("/dashboard/invoices/new");

    // Try to submit without filling the form
    await page.click("button[type='submit']");

    // Should show validation errors
    await expect(page.locator("text=Supplier is required")).toBeVisible();
    await expect(page.locator("text=Due date is required")).toBeVisible();
    await expect(page.locator("text=Amount must be a positive number")).toBeVisible();

    // Should not navigate away from the form
    await expect(page).toHaveURL("/dashboard/invoices/new");

    // Fill in just the supplier field
    await page.fill("input[name='supplier']", "Test Supplier");

    // Submit again
    await page.click("button[type='submit']");

    // Should still show some validation errors
    await expect(page.locator("text=Due date is required")).toBeVisible();
    await expect(page.locator("text=Amount must be a positive number")).toBeVisible();

    // Supplier error should be gone
    await expect(page.locator("text=Supplier is required")).not.toBeVisible();

La prueba anterior es excelente, ya que muestra un formulario que se intenta enviar sin datos.

Personalmente, me encanta hacer esto con Cursor, ya que piensa en todo tipo de casos de uso extraños (y realistas).

¿Qué ocurre si una prueba no se supera?

Aquí es donde este enfoque destaca.

Si se produce un error, es estupendo, ya que Cursor tiene el contexto exacto del caso de prueba sobre el error y, si lo combinas con la descripción general de tu proyecto, verás que empieza a corregir los errores por sí solo.

El cursor seguirá adelante y normalmente arreglará las cosas por sí solo.

Bajo el capó hay una carpeta llamada «datos de prueba» y, si inspeccionas esa carpeta, podrás ver un registro mucho más completo de la prueba y por qué falló.

- navigation:
    - text: LedgerLite
    - list:
        - listitem:
            - link "Dashboard":
                - /url: /dashboard
        - listitem:
            - link "Invoices":
                - /url: /dashboard/invoices
        - listitem:
            - link "Purchase Orders":
                - /url: /dashboard/pos
- main:
    - heading "Purchase Orders" [level=1]
    - link "New Purchase Order":
        - /url: /dashboard/pos/new
    - textbox "Search by supplier..."
    - table:
        - rowgroup:
            - row "Supplier Reference Amount Status Actions":
                - cell "Supplier"
                - cell "Reference"
                - cell "Amount"
                - cell "Status"
                - cell "Actions"
        - rowgroup:
            - row "No purchase orders found. Create your first purchase order!":
                - cell "No purchase orders found. Create your first purchase order!"
    - link "< Back to Dashboard":
        - /url: /dashboard
- alert
- button "Open Next.js Dev Tools":
    - img

También conoce el origen de la prueba y dónde falló.

import { test, expect } from "@playwright/test";

test("User can navigate to purchase orders page", async ({ page }) => {
  // Start from the dashboard
  await page.goto("/dashboard");

  // Navigate to purchase orders - increase timeout for navigation
  await page.click("text=Purchase Orders", { timeout: 10000 });
  await expect(page).toHaveURL("/dashboard/pos");

  // Wait for the page to load completely
  await page.waitForLoadState('networkidle');

  // Verify the page has loaded correctly - increase timeout for visibility check
  await expect(page.locator("h1")).toContainText("Purchase Orders", { timeout: 10000 });
  await expect(page.locator("a:has-text('New Purchase Order')")).toBeVisible({ timeout: 10000 });
});

También proporciona una descripción del error.

# Error details

Error: Timed out 10000ms waiting for expect(locator).toBeVisible()

Locator: locator('text=Status Test 1745482362373')
Expected: visible
Received: <element(s) not found>
Call log:
- expect.toBeVisible with timeout 10000ms
- waiting for locator('text=Status Test 1745482362373')

    at C:\Users\User\Documents\GitHub\testrepo\playwright-tests\purchase-order-status.spec.ts:80:56

Todo esto es mucho más limpio que el proceso de encontrar un error, copiar el código aleatorio e intentar decirle a Cursor que llegue al origen del error.

Aquí hay un buen ejemplo: mi API de órdenes de compra falló.

El cursor es mejor para navegar y resolver errores contextualmente.

Cursor probó una consulta en otro archivo que funcionaba y entonces se dio cuenta de por qué fallaba la API. No tuve que decirle nada.

Esto es mucho mejor para los LLM, ya que a veces un LLM arregla una cosa y luego estropea otras.

En la imagen anterior, al intentar corregir las órdenes de compra, se acabó interrumpiendo el proceso de creación de facturas.

Esto resulta muy útil, ya que permite probar todo el producto y detecta errores molestos que quizá no se te ocurriría probar en un primer momento. Además, permite a la IA comprender por qué ese cambio radical era negativo.

El cursor solo obtendrá los resultados finales y los utilizará para guiarse.

Recuerda que durante todo este tiempo solo se trata de sugerir y corregir, básicamente he estado en piloto automático todo este tiempo.

Creo que esto ha sido mucho más eficaz a la hora de resolver flujos de pruebas complicados de múltiples pasos.

Los datos de prueba son reales y se muestran en tu interfaz

Es muy satisfactorio ver cómo tu producto se va llenando con todos estos datos.

Todos los datos ficticios que crearon las pruebas.

Si estás creando cualquier tipo de sistema complejo y lo estás probando manualmente, esto te encantará. Se acabaron los clics repetitivos.

Vamos a trabajar con un ejemplo de código sencillo y lo explicaremos, y luego mostraremos cómo funciona con Cursor.

Tengo otra teoría: también es una forma mucho más rentable de desarrollar, ya que el LLM realiza muchas más solicitudes de herramientas por cada comando que le das.

Una gran ventaja que he descubierto es que se gasta menos energía mental en Cursor.

He descubierto que, cuando se realizan estas pruebas unitarias, se dedica mucho menos tiempo a introducir comandos y mucho más tiempo a dejar que la IA haga su trabajo.

Esto hace que sea mucho más fácil ejecutar varios proyectos en paralelo.

Por ejemplo, el error anterior es molesto de probar, pero este proceso ayuda mucho, ya que realiza la navegación por varias páginas por ti.

Las pruebas automatizadas son excelentes cuando se inicia un nuevo hilo de chat

¿Alguna vez te cansas de iniciar un nuevo hilo de chat y tener que pasar el contexto de nuevo y «calentar» el LLM para que entienda lo que estás haciendo?

Las pruebas son excelentes para esto, simplemente indícale que ejecute las pruebas de extremo a extremo cuando abras la ventana de chat y el modelo verá inmediatamente las cosas que no funcionan y sabrá exactamente qué hay que arreglar.

A menudo solo escribo esto y dejo que Cursor lo resuelva todo.

Ahora es una de mis cosas favoritas.

Lo bueno es que, cuando se completa una prueba, se abre una nueva pestaña que muestra los resultados.

Ni siquiera tienes que tener Cursor abierto, solo tienes que esperar a que la nueva pestaña llame tu atención.

Finalmente, tras unos 15 minutos, Cursor resolvió todos sus problemas y recibí un agradable mensaje con una luz verde.

Ahora puedo abrir mi producto y ver los resultados.

Una pantalla de facturación limpia y agradable que funciona a la primera y me lleva a la pantalla de la lista de facturas.
Es fantástico ver cómo el producto prueba múltiples estados y colores.
Una gran sensación cuando se superan todas las pruebas.

Puntos clave

  • Pide a Cursor que te escriba un conjunto de pruebas exhaustivas.
  • Piensa críticamente cuáles son importantes y empieza por ahí.
  • Si tienes un producto enorme, divide tus pruebas en unidades más pequeñas y resuelve una sección cada vez.
  • Este es un marco ideal si estás empezando un nuevo repositorio desde cero con un LLM.

Las ventajas de este enfoque

  • Después de escribir un par de pruebas pequeñas, verás las ventajas.
  • Ayudan a que tu código base se sienta más estable.
  • Las cosas van un poco más lentas, pero todo se siente más ordenado.
  • La IA tiene mucho más contexto.
  • Maximizas el uso de la herramienta y, en última instancia, te cuesta menos en reelaboraciones o refactorizaciones.

Las desventajas de este enfoque.

El desarrollo basado en pruebas puede ser lento, ya que hay que pasar por múltiples etapas antes de continuar. Si solo estás creando un prototipo y quieres obtener algo rápidamente, no siempre es necesario hacerlo.

En mi caso, la línea divisoria es que si estoy trabajando en un proyecto o una función en la que ni siquiera conozco el resultado final y utilizo la IA para ayudarme a averiguarlo, es menos probable que utilice este enfoque, ya que, en esencia, hay muchas cosas que podrían cambiar.

Si estoy trabajando en algo que tenga que ver remotamente con un proyecto para un cliente o con algo de lo que conozco la respuesta de antemano y tengo una idea general de cómo debería funcionar el proyecto, lo implementaré desde el principio.

Gracias por leer Código en Casa.