# Cypress: qué es, cómo se instala y cómo se usa

**Categoría:** Testing y QA · **Nivel:** Inicial · **Lectura:** 14 min
**Publicada:** 2026-07-27 · **Actualizada:** 2026-07-27 · **Autoría:** Underc0de
**Versión HTML (canónica):** https://underc0de.org/guias/testing/como-instalar-y-usar-cypress/

Qué es Cypress, requisitos e instalación con npm, la estructura que crea, tu primer test, el encadenamiento de comandos, reintentos automáticos y ejecución en CI.

## 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.

## 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.

> **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](../selenium-cypress-playwright/index.md). 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:

```bash
# 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:

```bash
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.](../../assets/img/guias/como-instalar-y-usar-cypress-estructura.svg)

*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
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
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.

| En lugar de esto | Escribí esto |
|---|---|
| `cy.wait(3000)` antes de comprobar | `cy.get(sel).should("be.visible")` |
| Guardar el elemento y reusarlo después | Volver a consultar con `cy.get()` |
| Subir `defaultCommandTimeout` a 30 s global | `cy.get(sel, { timeout: 10000 })` solo donde hace falta |
| Esperar un tiempo fijo tras una petición | `cy.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.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:

```bash
# 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
"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 JavaScript.** El 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 vez.** No 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 prueba.** Para 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óviles.** No 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](https://docs.cypress.io/app/get-started/install-cypress). Comandos por gestor de paquetes, requisitos de sistema y aviso sobre el bloqueo de scripts `postinstall`.
2. **Cypress.** [Why Cypress?](https://docs.cypress.io/app/get-started/why-cypress). Arquitectura de ejecución dentro del navegador, en el mismo run loop que la aplicación.
3. **Cypress.** [Writing and Organizing Tests](https://docs.cypress.io/app/core-concepts/writing-and-organizing-tests). Estructura de carpetas, patrón de nombres de las especificaciones y *hooks* disponibles.
4. **Cypress.** [Retry-ability](https://docs.cypress.io/app/core-concepts/retry-ability). Qué reintenta, el valor por defecto de 4 segundos y por qué no conviene guardar elementos en variables.
5. **Cypress.** [Trade-offs](https://docs.cypress.io/app/references/trade-offs). Limitaciones permanentes y temporales declaradas por el proyecto.
6. **Cypress.** [Continuous Integration](https://docs.cypress.io/app/continuous-integration/overview). Ejecución con `cypress run` en servidores de integración continua.
7. **Cypress.** [Changelog](https://docs.cypress.io/app/references/changelog) y [archivo de licencia del repositorio](https://github.com/cypress-io/cypress/blob/develop/LICENSE). 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)»](https://underc0de.org/foro/qa-testing/04-crear-un-ejemplo-real-con-cypress-basico/), 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.

## Guías relacionadas

- [Selenium, Cypress o Playwright: cuál elegir y por qué](../selenium-cypress-playwright/index.md) — la comparativa entre las tres.
- [Selenium: qué es, cómo se instala y cómo se usa](../como-instalar-y-usar-selenium/index.md)
- [Playwright: qué es, cómo se instala y cómo se usa](../como-instalar-y-usar-playwright/index.md)
- [Testing de software: guía para empezar en QA](../introduccion-al-testing/index.md) — qué automatizar y qué no.
- [Guías de Testing y QA](../index.md)

---

Esta guía forma parte de un proyecto de la comunidad Underc0de para organizar conocimiento técnico en español. Fuente canónica: https://underc0de.org/guias/testing/como-instalar-y-usar-cypress/
