system onlinepath: /guias/testing/como-instalar-y-usar-cypress/mode: knowledge_baselocal:
Testing y QA · Nivel inicial

Cypress: qué es, cómo se instala y cómo se usa

Se instala con un comando de npm y trae ejecutor, aserciones e informes en la misma caja. Su particularidad es que la prueba corre dentro del navegador: eso hace la depuración muy cómoda y define, al mismo tiempo, sus cuatro límites.

14 min de lectura▣ Actualizada el ◇ Por Underc0de
Respuesta rápida

Cypress es una plataforma de pruebas para aplicaciones web que se instala como dependencia de desarrollo con npm install cypress --save-dev y se abre con npx cypress open. A diferencia de las otras herramientas, ejecuta tu código dentro del propio navegador, en el mismo run loop que la aplicación: por eso puede mostrarte la app viva detenida en el paso que falló. Trae ejecutor de pruebas, aserciones, reintentos automáticos e informes incluidos. El costo de ese diseño es que las pruebas se escriben solo en JavaScript y que hay cuatro límites documentados que conviene conocer antes de empezar.

Ver índice de contenidos
  1. 01Qué es Cypress y en qué se distingue
  2. 02Requisitos antes de instalar
  3. 03Instalación y primera apertura
  4. 04La estructura que genera
  5. 05Tu primer test y las tres fases
  6. 06Reintentos: por qué no hay que esperar a mano
  7. 07Selectores y comandos propios
  8. 08Ejecutarlo en integración continua
  9. 09Los cuatro límites que hay que conocer
  10. 10Errores frecuentes al empezar
  11. 11Preguntas frecuentes
  12. 12Fuentes

Qué es Cypress y en qué se distingue

Cypress es una plataforma de pruebas para aplicaciones web modernas que combina pruebas de extremo a extremo, pruebas de componentes y verificaciones de accesibilidad en un mismo flujo de trabajo. Se instala en el proyecto, se ejecuta localmente y en integración continua, y la aplicación de código abierto está publicada con licencia MIT.

Lo que lo diferencia está en la arquitectura. Su documentación lo plantea como una inversión deliberada: la mayoría de las herramientas «operan corriendo fuera del navegador y ejecutando órdenes remotas por la red», mientras Cypress se ejecuta «en el mismo run loop que tu aplicación». Hay un proceso de Node acompañando al navegador, de modo que la herramienta ve las dos mitades del sistema a la vez y puede acceder al window, al document, al DOM y a los temporizadores de la página.

i
Lo que eso te da en la práctica

Cuando una prueba falla, no ves solo un mensaje de error: ves la aplicación congelada en ese instante, con el historial de comandos al costado, y podés retroceder paso a paso e inspeccionar el DOM tal como estaba. Es la mejor experiencia de depuración de las tres herramientas, y la razón principal por la que la gente lo elige.

Si estás decidiendo todavía entre esta herramienta y las otras dos, la comparación completa está en Selenium, Cypress o Playwright. Acá damos por hecho que ya elegiste Cypress.

Requisitos antes de instalar

La documentación oficial declara estos requisitos de sistema:

  • Node.js en las versiones 20.x, 22.x o 24 y superiores.
  • Un gestor de paquetes: npm ≥ 10.1.0, Yarn 1 ≥ 1.22.22, Yarn Modern ≥ 4.x, pnpm ≥ 8.x o Bun ≥ 1.2.22.
  • Sistema operativo: macOS ≥ 13.5 (Intel o Apple Silicon), Linux x64 o arm64 (Ubuntu ≥ 22.04, Debian ≥ 11, Fedora ≥ 43), Windows 10 u 11 en x64, o Windows Server 2019, 2022 y 2025.
  • Un proyecto con package.json. Si estás arrancando de cero, npm init -y lo crea.

No hace falta instalar navegadores por separado: Cypress trae Electron incorporado y puede usar el Chrome, Edge o Firefox que ya tengas.

Instalación y primera apertura

Cypress se instala como dependencia de desarrollo, porque no forma parte de lo que se publica en producción:

Instalación por gestor
# npm
npm install cypress --save-dev

# Yarn
yarn add cypress --dev

# pnpm
pnpm add --save-dev cypress

# Bun
bun add --dev cypress
!
Si la instalación termina pero Cypress no arranca

La documentación avisa de un problema cada vez más frecuente: «los gestores de paquetes bloquean cada vez más los scripts de ciclo de vida de dependencias, como postinstall, por defecto». Cypress descarga su binario en ese paso, así que con el bloqueo activo el paquete queda instalado pero sin ejecutable. Cada gestor documenta su forma de permitirlo; en pnpm, por ejemplo, se declara el paquete en la lista de dependencias con scripts aprobados.

Con el paquete instalado, se abre la aplicación por primera vez:

Terminal
npx cypress open

Esa primera apertura es la que hace el trabajo de andamiaje: te pregunta si querés configurar pruebas E2E o de componentes, y al elegir crea las carpetas y el archivo de configuración. No hay que armar la estructura a mano.

La estructura que genera

Vale la pena entender qué es cada carpeta antes de escribir la primera prueba, porque el reparto de responsabilidades es bastante estricto.

A la izquierda, el árbol de carpetas de Cypress: cypress.config.js en la raíz y la carpeta cypress con las subcarpetas e2e, fixtures, support, screenshots y videos. A la derecha, una ficha por carpeta explicando su función, el patrón de nombres punto cy punto js obligatorio para las especificaciones, y los dos modos de ejecución: cypress open con interfaz y cypress run sin ventana.
La estructura la crea Cypress en la primera apertura. El patrón de nombres de los archivos de prueba no es opcional.

Hay un detalle que hace perder media hora a casi todo el mundo la primera vez: Cypress solo descubre los archivos que coinciden con el patrón **/*.cy.{js,jsx,ts,tsx}. La documentación lo dice con un ejemplo que no deja lugar a dudas: un archivo llamado cypress/e2e/login.js no se descubre, mientras cypress/e2e/login.cy.js sí.

El archivo de configuración en la raíz se llama cypress.config.js —o .ts en proyectos TypeScript— y es donde se define la URL base del proyecto, algo que conviene hacer desde el principio:

JavaScript · cypress.config.js
const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    // Con esto, cy.visit("/") ya apunta acá
    baseUrl: "https://underc0de.org/guias",
    // 4000 ms es el valor por defecto; subilo solo si hace falta
    defaultCommandTimeout: 4000,
    video: false
  }
});

Definir baseUrl evita repetir el dominio en cada prueba y, sobre todo, permite apuntar la misma suite a local, a preproducción o a producción cambiando una variable de entorno.

Tu primer test y las tres fases

Cypress hereda la estructura de Mocha: describe() agrupa, it() define cada prueba, y los hooks before(), beforeEach(), afterEach() y after() manejan la preparación y la limpieza.

Dentro de cada prueba, el patrón recomendado tiene tres fases: preparar el estado de la aplicación, ejecutar una acción y afirmar sobre el estado resultante. Es el mismo esquema que en otros contextos se llama arrange, act, assert o given, when, then.

JavaScript · cypress/e2e/busqueda.cy.js
describe("Buscador del índice de guías", () => {
  beforeEach(() => {
    // 1. Preparar: cada prueba arranca desde un estado conocido
    cy.visit("/");
  });

  it("filtra a una sola guía al buscar «wardriving»", () => {
    // 2. Actuar
    cy.get("[data-guide-search]").type("wardriving");

    // 3. Afirmar — should() reintenta hasta que se cumpla
    cy.get("[data-result-count]").should("have.text", "1 guía encontrada");
    cy.get("[data-guide-card]:visible").should("have.length", 1);
  });

  it("muestra el mensaje de vacío cuando no hay coincidencias", () => {
    cy.get("[data-guide-search]").type("zzzzz");
    cy.get("[data-result-count]").should("have.text", "0 guías encontradas");
  });
});

Un par de cosas a notar. Los comandos se encadenan: cy.get(...) devuelve algo sobre lo que se puede seguir operando, y eso hace que la prueba se lea casi como una narración de lo que hace una persona. Y no hay ninguna espera escrita a mano, aunque el filtrado ocurre de forma asincrónica: de eso se encarga el mecanismo de reintentos, que es el tema de la sección siguiente.

Fijate también en el segundo caso: probar el camino que falla es tan importante como probar el que funciona. Buscar algo inexistente y verificar el mensaje de «sin resultados» cubre una rama del código que el camino feliz nunca toca.

Reintentos: por qué no hay que esperar a mano

Esta es la mecánica que más conviene entender bien, porque explica tanto la estabilidad de Cypress como los errores más difíciles de diagnosticar.

El principio es que las consultas reintentan y las acciones no. Cuando escribís cy.get("[data-result-count]").should("have.text", "1 guía encontrada") y la aserción falla, Cypress no se rinde: vuelve a consultar el DOM desde el comienzo de la cadena de consultas y lo intenta de nuevo, hasta que la aserción pase o se agote el tiempo. El plazo por defecto es de 4 segundos, configurable con defaultCommandTimeout.

Las acciones como .click() son distintas: esperan a que el elemento sea accionable, pero se ejecutan una sola vez y no reintentan después.

!
El antipatrón que rompe los reintentos

Guardar un elemento en una variable congela esa referencia. Si la aplicación se vuelve a renderizar, el elemento se desprende del DOM y la referencia queda huérfana. Por eso la forma correcta no es guardar y reutilizar, sino volver a consultar: es lo que permite que cada reintento trabaje con el DOM actual.

Comparación entre esperar a mano y usar los reintentos de Cypress
En lugar de estoEscribí esto
cy.wait(3000) antes de comprobarcy.get(sel).should("be.visible")
Guardar el elemento y reusarlo despuésVolver a consultar con cy.get()
Subir defaultCommandTimeout a 30 s globalcy.get(sel, { timeout: 10000 }) solo donde hace falta
Esperar un tiempo fijo tras una peticióncy.intercept() y esperar el alias de esa petición

Ese último caso merece una aclaración: cy.wait() con un número de milisegundos es casi siempre un parche, pero cy.wait() con el alias de una petición interceptada es una herramienta legítima y precisa, porque espera exactamente hasta que esa petición terminó.

Selectores y comandos propios

Cypress consulta el DOM con selectores de CSS mediante cy.get(), y con texto visible mediante cy.contains(). La recomendación de fondo es la misma que en cualquier herramienta: apoyarse en atributos puestos a propósito para las pruebas, del estilo data-test="enviar", en lugar de clases de CSS que existen para dar estilo y cambian cuando alguien retoca el diseño.

Cuando una secuencia se empieza a repetir en muchas pruebas —el login es el ejemplo canónico— se extrae a un comando propio en cypress/support/commands.js:

JavaScript · cypress/support/commands.js
Cypress.Commands.add("buscarGuia", (texto) => {
  cy.get("[data-guide-search]").clear().type(texto);
  return cy.get("[data-result-count]");
});

// Y en cualquier prueba, ahora alcanza con:
// cy.buscarGuia("wardriving").should("have.text", "1 guía encontrada");

Es el equivalente en Cypress al Page Object Model de Selenium: el selector vive en un solo lugar y las pruebas hablan del dominio, no del HTML.

Ejecutarlo en integración continua

Cypress tiene dos modos y la misma suite corre en los dos sin modificaciones:

Terminal
# Con interfaz: para escribir y depurar
npx cypress open

# Sin ventana: para la terminal y para CI
npx cypress run

# Solo un archivo, en un navegador concreto
npx cypress run --spec "cypress/e2e/busqueda.cy.js" --browser chrome

La documentación describe la ejecución en CI como «casi lo mismo que ejecutarlo localmente en tu terminal», y en efecto cypress run es todo lo que hace falta: devuelve un código de salida distinto de cero si algo falla, que es lo que el servidor usa para marcar la construcción como fallida.

Conviene añadir los atajos al package.json para que nadie tenga que recordar los comandos:

JSON · package.json
"scripts": {
  "test:abrir": "cypress open",
  "test:e2e": "cypress run"
}

Dos detalles operativos: agregá cypress/screenshots/ y cypress/videos/ al .gitignore, porque son artefactos generados y no deben viajar en el repositorio; y en CI conviene publicarlos como archivos adjuntos de la construcción, que es lo que después te permite entender un fallo sin reproducirlo.

Los cuatro límites que hay que conocer

Cypress publica una página donde separa las limitaciones permanentes —consecuencia directa de su arquitectura— de las temporales. Estas son las permanentes, y es mejor conocerlas ahora que a los tres meses:

  1. Solo JavaScriptEl código corre en el navegador. Para hablar con el backend o la base de datos hay que salir por cy.exec(), cy.task() o cy.request().
  2. Un navegador a la vezNo controla más de un navegador abierto simultáneamente, así que los escenarios de colaboración entre dos usuarios reales quedan fuera.
  3. Un superdominio por pruebaPara cruzar a otro origen hay que usar cy.origin(). Relevante si tu flujo redirige a una pasarela de pago o a un proveedor de identidad externo.
  4. Sin eventos nativos ni móvilesNo hay eventos del sistema operativo ni gestos móviles, y los iframe de otro origen tienen soporte parcial.

La documentación defiende varias de estas restricciones como una forma de empujar hacia pruebas más rápidas y menos frágiles, y el argumento tiene sentido. Pero conviene contrastarlo con tu producto concreto: si dos de los cuatro puntos te afectan, la herramienta te va a costar trabajo extra todos los meses.

Errores frecuentes al empezar

  • Nombrar el archivo sin .cy. login.js no se descubre; login.cy.js sí. Es la causa número uno de «no me aparece la prueba».
  • Instalar y que el binario no baje. Si el gestor de paquetes bloquea los scripts postinstall, el paquete queda sin ejecutable. Revisá la sección de tu gestor en la documentación de instalación.
  • Llenar la suite de cy.wait(3000). Hace la suite lenta y sigue fallando. Usá aserciones, que reintentan, o interceptá la petición y esperá su alias.
  • Subir el timeout global para tapar intermitencias. Convierte cada fallo real en una espera de treinta segundos. Subilo puntualmente donde haga falta.
  • Encadenar todo desde una variable guardada. Genera referencias huérfanas cuando la app se vuelve a renderizar.
  • Depender del orden entre pruebas. Cada it() debe poder correr solo. Si la segunda prueba necesita lo que dejó la primera, vas a tener fallos imposibles de reproducir.
  • Subir videos y capturas al repositorio. Se generan en cada corrida y pesan; van al .gitignore.

Preguntas frecuentes

¿Puedo usar Cypress con Python o Java?

No. Es la limitación más importante de Cypress y no tiene rodeo: como el código de la prueba se ejecuta dentro del navegador, el único lenguaje posible es JavaScript, con TypeScript como variante. Si tu equipo escribe Python, Java o C# y quiere mantener las pruebas en su mismo lenguaje, las opciones son Selenium o Playwright.

¿Por qué Cypress no encuentra mi archivo de prueba?

Casi seguro por el nombre. Cypress solo reconoce como especificación los archivos que coinciden con el patrón .cy.js, .cy.jsx, .cy.ts o .cy.tsx. La documentación lo señala con un ejemplo directo: un archivo llamado login.js no se descubre, mientras login.cy.js sí. Renombralo y aparece.

¿Cuál es la diferencia entre cypress open y cypress run?

cypress open abre la aplicación con interfaz gráfica: ves la app a la izquierda, la lista de comandos a la derecha y podés recorrer cada paso. Es el modo para escribir y depurar. cypress run ejecuta todo sin ventana desde la terminal y devuelve un código de salida, que es lo que necesita un servidor de integración continua. La misma suite corre en los dos modos sin cambios.

¿Hay que pagar para usar Cypress?

No para las pruebas en sí. La aplicación de Cypress es software libre con licencia MIT y podés instalarla y ejecutarla sin costo, en local y en CI. Aparte existe Cypress Cloud, un servicio comercial de la misma empresa que guarda el histórico de ejecuciones, permite paralelizar de forma coordinada y graba las corridas. Es opcional: la suite funciona completa sin él.

¿Por qué no debería guardar elementos en variables?

Porque rompe los reintentos, que son lo que hace estable a Cypress. Cuando escribís cy.get(...).should(...), si la aserción falla Cypress vuelve a ejecutar toda la cadena de consultas desde el principio, con el DOM actualizado. Si en cambio guardás el elemento en una variable, esa referencia queda congelada y puede quedar huérfana si la aplicación se vuelve a renderizar. La forma correcta es volver a consultar cada vez.

¿Cypress puede probar dos pestañas o dos dominios?

Con límites que conviene conocer antes de empezar. Cypress no controla más de un navegador abierto a la vez, así que los escenarios entre dos usuarios simultáneos quedan fuera. Y cada prueba está atada a un superdominio: para navegar a otro origen hay que usar el comando cy.origin(). Si tu aplicación depende de redirecciones a terceros, como una pasarela de pago, medí ese trabajo extra antes de decidir.

Fuentes

Documentación oficial y registros de paquetes consultados para esta guía. Fecha de consulta: 27 de julio de 2026.

  1. Cypress. Install Cypress. Comandos por gestor de paquetes, requisitos de sistema y aviso sobre el bloqueo de scripts postinstall.
  2. Cypress. Why Cypress?. Arquitectura de ejecución dentro del navegador, en el mismo run loop que la aplicación.
  3. Cypress. Writing and Organizing Tests. Estructura de carpetas, patrón de nombres de las especificaciones y hooks disponibles.
  4. Cypress. Retry-ability. Qué reintenta, el valor por defecto de 4 segundos y por qué no conviene guardar elementos en variables.
  5. Cypress. Trade-offs. Limitaciones permanentes y temporales declaradas por el proyecto.
  6. Cypress. Continuous Integration. Ejecución con cypress run en servidores de integración continua.
  7. Cypress. Changelog y archivo de licencia del repositorio. Versión 15.19.0 y licencia MIT vigentes al 27 de julio de 2026.
  8. Underc0de, foro. «04 - Crear un ejemplo real con Cypress (Básico)», por Mr. Bones, 26 de agosto de 2023, sección QA. Aporte de la comunidad donde aparece el patrón de tres fases que usa esta guía.