TL;DR
- Cypress corre dentro del navegador, haciendo los tests rápidos y confiables sin WebDriver
- Instala con
npm install cypress --save-dev, luego ejecutanpx cypress open- Usa atributos
data-*para selectores — sobreviven cambios de UIIdeal para: Principiantes aprendiendo automatización, equipos testeando apps web JavaScript No es para ti si: Necesitas testing de apps móviles o soporte de Safari out of the box
Recuerdo mi primera semana intentando automatizar tests de navegador con Selenium. Archivos de configuración, descargas de drivers, mensajes de error oscuros sobre session IDs. Pasé más tiempo debuggeando mi setup de tests que escribiendo tests reales. Cuando descubrí Cypress, todo cambió. Tenía un test funcionando en 15 minutos.
Cypress se ha convertido en la opción preferida para testear aplicaciones web modernas. Corre directamente dentro del navegador, proporciona feedback instantáneo y requiere configuración mínima. Esta guía te lleva desde la instalación hasta correr tests en pipelines de CI/CD.
¿Por qué Cypress?
Antes de escribir código, entendamos qué hace diferente a Cypress de las herramientas tradicionales.
Cypress vs Herramientas Tradicionales:
| Característica | Cypress | Selenium/WebDriver |
|---|---|---|
| Arquitectura | Corre dentro del navegador | Proceso de driver externo |
| Tiempo de setup | Minutos | Horas (drivers, configs) |
| Debugging | Time-travel, snapshots | Screenshots, logs |
| Velocidad | Rápido (sin saltos de red) | Más lento (comandos HTTP) |
| Flakiness | Bajo (esperas automáticas) | Mayor (esperas manuales) |
Cypress sobresale testeando aplicaciones single-page construidas con React, Vue, Angular o frameworks similares. Proporciona recarga en tiempo real, esperas automáticas y una experiencia de debugging que se siente como usar DevTools del navegador.
Si estás construyendo una estrategia de testing comprehensiva, entender la pirámide de automatización de tests te ayuda a decidir dónde encaja Cypress junto a unit tests y API tests.
Instalando Cypress
Prerrequisitos
Necesitas Node.js instalado en tu máquina. Cualquier versión desde 18.x funciona bien.
# Verificar versión de Node.js
node --version
# Debería mostrar v18.x.x o superior
Pasos de Instalación
1. Inicializar un proyecto (si no tienes uno):
mkdir my-cypress-tests
cd my-cypress-tests
npm init -y
2. Instalar Cypress:
npm install cypress --save-dev
Esto descarga Cypress y su navegador Electron. La primera instalación toma un par de minutos.
3. Abrir Cypress:
npx cypress open
Esto lanza el Cypress Test Runner — una interfaz visual para correr tests.
Primera Experiencia de Lanzamiento
Cuando abres Cypress por primera vez, crea una estructura de carpetas:
cypress/
├── e2e/ # Tus archivos de test van aquí
├── fixtures/ # Datos de test (archivos JSON)
├── support/ # Comandos personalizados y setup
│ ├── commands.js # Comandos personalizados
│ └── e2e.js # Se ejecuta antes de cada archivo de test
└── downloads/ # Archivos descargados durante tests
Cypress también crea cypress.config.js en la raíz de tu proyecto — este es tu archivo de configuración principal.
Escribiendo Tu Primer Test
Escribamos un test que visita una página y verifica su contenido. Crea un archivo cypress/e2e/first-test.cy.js:
describe('Mi Primer Test', () => {
it('visita la página de ejemplo', () => {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
cy.url().should('include', '/commands/actions')
cy.get('.action-email')
.type('test@example.com')
.should('have.value', 'test@example.com')
})
})
Desglose:
describe()agrupa tests relacionadosit()define un caso de test individualcy.visit()navega a una URLcy.contains()encuentra elemento por contenido de textocy.get()encuentra elemento por selector CSS.type()ingresa texto en un input.should()hace una aserción
Ejecuta este test haciendo clic en first-test.cy.js en el Test Runner.
Entendiendo la Estructura de Tests
Cada test de Cypress sigue un patrón:
describe('Nombre de Feature', () => {
beforeEach(() => {
// Se ejecuta antes de cada test
cy.visit('/login')
})
it('hace algo específico', () => {
// Arrange: configura condiciones del test
// Act: realiza la acción
// Assert: verifica el resultado
})
it('maneja otro escenario', () => {
// Otro caso de test
})
})
El hook beforeEach se ejecuta antes de cada test en el bloque describe. Úsalo para setup común como login o navegación a una página.
Selectores: Encontrando Elementos
Encontrar los elementos correctos es crucial para tests confiables. Cypress soporta varias estrategias de selectores.
Prioridad de Selectores (Mejor a Peor)
1. Atributos data (recomendado):
cy.get('[data-test="submit-button"]')
cy.get('[data-cy="login-form"]')
cy.get('[data-testid="user-email"]')
Los atributos data existen específicamente para testing. No cambian cuando cambian los estilos.
2. Selectores ID:
cy.get('#username')
Los IDs son estables pero no siempre están disponibles. No agregues IDs solo para testing — usa atributos data en su lugar.
3. Contenido de texto:
cy.contains('Enviar')
cy.contains('button', 'Enviar') // Más específico
Bueno para botones y enlaces. Se rompe si el texto cambia o se traduce.
4. Selectores CSS (evitar si es posible):
cy.get('.btn-primary')
cy.get('form input[type="email"]')
Las clases CSS cambian frecuentemente. Selectores complejos son frágiles.
Agregando Atributos de Test a Tu App
Trabaja con tu equipo de desarrollo para agregar atributos de test:
<!-- Antes -->
<button class="btn btn-primary">Registrarse</button>
<!-- Después -->
<button class="btn btn-primary" data-test="signup-button">Registrarse</button>
El atributo data-test sobrevive refactorizaciones, cambios de temas y actualizaciones de CSS.
Cypress Testing Library
Para mejores selectores basados en accesibilidad, instala Testing Library:
npm install @testing-library/cypress --save-dev
Agrega a cypress/support/commands.js:
import '@testing-library/cypress/add-commands'
Ahora puedes usar selectores accesibles:
cy.findByRole('button', { name: 'Enviar' })
cy.findByLabelText('Dirección de Email')
cy.findByPlaceholderText('Ingresa tu email')
Estos selectores coinciden con cómo los usuarios interactúan con tu app.
Aserciones y Expectativas
Cypress usa aserciones Chai. Los patrones más comunes:
Aserciones Should
// Visibilidad
cy.get('[data-test="header"]').should('be.visible')
cy.get('[data-test="modal"]').should('not.exist')
// Contenido
cy.get('[data-test="title"]').should('have.text', 'Bienvenido')
cy.get('[data-test="title"]').should('contain', 'Bienvenido')
// Atributos
cy.get('input').should('have.value', 'test@example.com')
cy.get('a').should('have.attr', 'href', '/dashboard')
// Estado
cy.get('button').should('be.disabled')
cy.get('input').should('be.enabled')
cy.get('checkbox').should('be.checked')
// Cantidad
cy.get('[data-test="list-item"]').should('have.length', 5)
cy.get('[data-test="list-item"]').should('have.length.gt', 3)
Encadenando Aserciones
cy.get('[data-test="user-card"]')
.should('be.visible')
.and('contain', 'Juan Pérez')
.and('have.class', 'active')
Expect para Verificaciones Complejas
cy.get('[data-test="product-list"]').then(($list) => {
const itemCount = $list.find('.product').length
expect(itemCount).to.be.greaterThan(0)
expect(itemCount).to.be.lessThan(100)
})
Interactuando con Elementos
Acciones Comunes
// Clicks
cy.get('button').click()
cy.get('button').dblclick()
cy.get('button').rightclick()
// Escribir
cy.get('input').type('Hola Mundo')
cy.get('input').type('Hola{enter}') // Presionar Enter
cy.get('input').clear().type('Nuevo texto')
// Seleccionar
cy.get('select').select('Opción 1')
cy.get('select').select(['Opción 1', 'Opción 2']) // Multi-select
// Checkboxes y Radios
cy.get('[type="checkbox"]').check()
cy.get('[type="checkbox"]').uncheck()
cy.get('[type="radio"]').check()
// Subir Archivos
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png')
// Scroll
cy.get('[data-test="footer"]').scrollIntoView()
Manejando Formularios
Un test de formulario típico:
describe('Formulario de Contacto', () => {
beforeEach(() => {
cy.visit('/contact')
})
it('envía exitosamente con datos válidos', () => {
cy.get('[data-test="name"]').type('María García')
cy.get('[data-test="email"]').type('maria@example.com')
cy.get('[data-test="message"]').type('¡Hola desde Cypress!')
cy.get('[data-test="submit"]').click()
cy.get('[data-test="success-message"]')
.should('be.visible')
.and('contain', 'Gracias')
})
it('muestra error para email inválido', () => {
cy.get('[data-test="email"]').type('email-invalido')
cy.get('[data-test="submit"]').click()
cy.get('[data-test="email-error"]')
.should('be.visible')
.and('contain', 'email válido')
})
})
Trabajando con APIs
Cypress puede interceptar y mockear llamadas API. Esto hace los tests más rápidos y confiables.
Interceptando Requests
cy.intercept('GET', '/api/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
cy.get('[data-test="user-list"]').should('be.visible')
El cy.wait('@getUsers') pausa hasta que la llamada API se complete. No más hacks con cy.wait(5000).
Mockeando Respuestas de API
cy.intercept('GET', '/api/products', {
statusCode: 200,
body: [
{ id: 1, name: 'Producto A', price: 29.99 },
{ id: 2, name: 'Producto B', price: 39.99 }
]
}).as('getProducts')
cy.visit('/products')
cy.wait('@getProducts')
cy.get('[data-test="product"]').should('have.length', 2)
Mockear te permite testear casos edge sin cambios en el backend.
Testeando Estados de Error
cy.intercept('POST', '/api/checkout', {
statusCode: 500,
body: { error: 'Pago fallido' }
}).as('checkoutError')
cy.get('[data-test="checkout-button"]').click()
cy.wait('@checkoutError')
cy.get('[data-test="error-message"]')
.should('be.visible')
.and('contain', 'Pago fallido')
Para cobertura profunda de técnicas de network stubbing, revisa Cypress Deep Dive: Arquitectura, Debugging y Network Stubbing.
Configuración de Tests
cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 10000,
video: false,
screenshotOnRunFailure: true,
setupNodeEvents(on, config) {
// Event listeners de Node aquí
}
}
})
Configuraciones comunes:
| Configuración | Descripción | Default |
|---|---|---|
baseUrl | Se antepone a URLs de cy.visit() | ninguno |
viewportWidth/Height | Dimensiones del navegador | 1000 × 660 |
defaultCommandTimeout | Tiempo de reintento de comandos | 4000ms |
video | Grabar videos de ejecuciones | true |
retries | Auto-reintentar tests fallidos | 0 |
Variables de Entorno
// cypress.config.js
module.exports = defineConfig({
e2e: {
env: {
apiUrl: 'http://localhost:4000',
adminUser: 'admin@test.com'
}
}
})
Acceso en tests:
cy.visit(Cypress.env('apiUrl') + '/login')
cy.get('input').type(Cypress.env('adminUser'))
O pasar vía línea de comandos:
npx cypress run --env apiUrl=http://staging.example.com
Comandos Personalizados
Crea comandos reutilizables en cypress/support/commands.js:
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login')
cy.get('[data-test="email"]').type(email)
cy.get('[data-test="password"]').type(password)
cy.get('[data-test="submit"]').click()
cy.url().should('include', '/dashboard')
})
Cypress.Commands.add('logout', () => {
cy.get('[data-test="user-menu"]').click()
cy.get('[data-test="logout"]').click()
})
Uso en tests:
describe('Dashboard', () => {
beforeEach(() => {
cy.login('user@example.com', 'password123')
})
it('muestra perfil de usuario', () => {
cy.get('[data-test="profile"]').should('be.visible')
})
})
Login Programático
Para tests más rápidos, salta el login de UI:
Cypress.Commands.add('loginByApi', (email, password) => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: { email, password }
}).then((response) => {
window.localStorage.setItem('authToken', response.body.token)
})
})
Esto es mucho más rápido que llenar formularios repetidamente.
Organizando Tests
Estructura de Carpetas
cypress/
├── e2e/
│ ├── auth/
│ │ ├── login.cy.js
│ │ └── registration.cy.js
│ ├── products/
│ │ ├── listing.cy.js
│ │ └── checkout.cy.js
│ └── smoke/
│ └── critical-paths.cy.js
├── fixtures/
│ ├── users.json
│ └── products.json
└── support/
├── commands.js
└── e2e.js
Usando Fixtures
Almacena datos de test en cypress/fixtures/:
// cypress/fixtures/users.json
{
"admin": {
"email": "admin@example.com",
"password": "admin123"
},
"regular": {
"email": "user@example.com",
"password": "user123"
}
}
Cargar en tests:
cy.fixture('users').then((users) => {
cy.login(users.admin.email, users.admin.password)
})
// O usar directamente en intercepts
cy.intercept('GET', '/api/users', { fixture: 'users.json' })
Ejecutando Tests en CI/CD
Ejecución por Línea de Comandos
# Ejecutar todos los tests
npx cypress run
# Ejecutar archivo específico
npx cypress run --spec "cypress/e2e/auth/*.cy.js"
# Ejecutar en navegador específico
npx cypress run --browser chrome
# Ejecutar con variables de entorno
npx cypress run --env apiUrl=http://staging.example.com
Integración con GitHub Actions
Crea .github/workflows/cypress.yml:
name: Cypress Tests
on: [push, pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Start application
run: npm start &
- name: Wait for app
run: npx wait-on http://localhost:3000
- name: Run Cypress tests
uses: cypress-io/github-action@v6
with:
wait-on: 'http://localhost:3000'
browser: chrome
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
Para más patrones de CI/CD, revisa GitHub Actions para Automatización QA.
Ejecución Paralela
Para test suites grandes, ejecuta tests en paralelo:
jobs:
cypress:
runs-on: ubuntu-latest
strategy:
matrix:
containers: [1, 2, 3]
steps:
- uses: cypress-io/github-action@v6
with:
record: true
parallel: true
group: 'UI Tests'
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Esto requiere Cypress Cloud (nivel gratuito disponible).
Debuggeando Tests Fallidos
Time Travel
Haz clic en cualquier comando en el Test Runner para ver el estado del DOM en ese momento. Esto es increíblemente útil para entender por qué un selector falló.
Screenshots y Videos
// Tomar screenshot manualmente
cy.screenshot('before-submit')
// Screenshots son automáticos al fallar
// Videos graban toda la ejecución del test
Configurar en cypress.config.js:
module.exports = defineConfig({
e2e: {
screenshotOnRunFailure: true,
video: true,
videosFolder: 'cypress/videos',
screenshotsFolder: 'cypress/screenshots'
}
})
Comando Debug
cy.get('[data-test="menu"]').debug()
Esto pausa la ejecución y loguea el elemento a la consola.
Logging a Consola
cy.get('[data-test="items"]').then(($items) => {
console.log('Cantidad de items:', $items.length)
console.log('Primer item:', $items.first().text())
})
Abre DevTools del navegador para ver el output.
Mejores Prácticas
Hacer
- Usar atributos
data-testpara selectores - Esperar llamadas API en vez de timeouts arbitrarios
- Mantener tests independientes (cada uno puede correr solo)
- Usar
beforeEachpara setup común - Testear una cosa por caso de test
- Mockear servicios externos
No Hacer
- Usar
cy.wait(5000)— esperar eventos específicos - Compartir estado entre tests
- Testear detalles de implementación (clases CSS, estructura HTML)
- Sobre-mockear — algunos tests deben ir a APIs reales
- Escribir tests enormes — mantenerlos enfocados
Manejando Tests Flaky
// Malo - dependiente del tiempo
cy.wait(3000)
cy.get('[data-test="results"]').should('exist')
// Bueno - espera condición específica
cy.intercept('GET', '/api/search*').as('search')
cy.get('[data-test="search"]').type('query')
cy.wait('@search')
cy.get('[data-test="results"]').should('exist')
IA en Cypress Testing
Las herramientas de IA cambiaron cómo escribo tests en Cypress. En mi equipo actual, redujimos el tiempo de creación de tests en aproximadamente un 40% después de integrar prompts de IA en nuestro flujo.
Lo que la IA hace bien:
- Generar boilerplate de tests desde user stories o criterios de aceptación
- Convertir casos de test manuales en código Cypress con selectores apropiados
- Sugerir edge cases que te perderías: estados vacíos, input Unicode, timeouts de red
- Depurar tests flaky analizando logs de comandos y sugiriendo fixes
- Crear datos de fixture desde schemas de respuestas API
Lo que aún necesita humanos:
- Elegir qué flujos automatizar vs mantener manuales
- Decidir estrategia de selectores (data-test vs roles de accesibilidad) para tu equipo
- Saber cuándo mockear APIs vs ir a endpoints reales
- Evaluar valor de tests — algunos cuestan más mantener que los bugs que detectarían
Prompt útil:
Tengo una página de login con input de email, input de password, checkbox “Recordarme” y botón submit. Todos los inputs tienen atributos data-test. Escribe tests de Cypress cubriendo: login exitoso, error de contraseña incorrecta, validación de campos vacíos y persistencia de “Recordarme” entre sesiones. Usa cy.intercept para mockear el API de auth.
FAQ
¿Cypress es gratis?
Sí, Cypress es open-source y completamente gratuito para desarrollo local y CI/CD. Cypress Cloud (anteriormente Dashboard) ofrece un nivel gratuito con grabaciones de test limitadas. Los niveles de pago proporcionan más ejecuciones paralelas, mayor retención de historial y analíticas avanzadas. La mayoría de los equipos empiezan gratis y actualizan según crecen los test suites.
¿Necesito saber JavaScript para usar Cypress?
Conocimientos básicos de JavaScript ayudan, pero no necesitas ser experto. La sintaxis de Cypress está diseñada para ser legible — cy.get('button').click() es comprensible incluso sin experiencia en JavaScript. Empieza con tests simples y aprende patrones de JavaScript (promises, arrow functions, destructuring) según los necesites. Muchos ingenieros QA aprenden JavaScript a través de Cypress.
¿Cypress puede testear cualquier sitio web?
Cypress testea aplicaciones web que corren en un navegador. Funciona con cualquier framework frontend: React, Vue, Angular, JavaScript vanilla, o páginas server-rendered. Cypress no puede testear apps móviles nativas (iOS/Android), aplicaciones de escritorio, o apps Electron en builds de producción. Para testing móvil, necesitas herramientas como Appium. Cypress también tiene limitaciones con iframes cross-origin y múltiples pestañas de navegador.
¿Cuánto tiempo toma aprender Cypress?
Puedes escribir y ejecutar tu primer test dentro de una hora de instalación. Dominio básico — escribir tests confiables, usar selectores apropiadamente, manejar formularios — toma 2-4 semanas de práctica regular. Habilidades avanzadas como comandos personalizados, mocking de API e integración CI/CD toman más tiempo. La mayoría de los ingenieros se sienten confiados después de automatizar su primera feature real de principio a fin.
¿Debería usar Cypress o Playwright en 2026?
Ambos son excelentes. Cypress tiene mejor debugging (time-travel), setup más rápido y ecosistema maduro de plugins. Playwright soporta más navegadores (Chromium, Firefox, WebKit/Safari), maneja escenarios multi-tab y corre más rápido en CI con workers paralelos. Recomiendo Cypress para equipos nuevos en automatización y Playwright para equipos que necesitan cobertura Safari o flujos complejos multi-página. Revisa mi comparación detallada Playwright vs Cypress para benchmarks.
¿Cypress puede correr en Docker y pipelines CI/CD?
Sí. Cypress provee imágenes Docker oficiales: cypress/included (Cypress pre-instalado) y cypress/browsers (múltiples navegadores). El GitHub Action cypress-io/github-action@v6 maneja instalación, caché y setup de navegador automáticamente. Cypress funciona con GitHub Actions, GitLab CI, Jenkins, CircleCI y cualquier entorno que soporte Node.js. Para suites grandes, Cypress Cloud habilita distribución paralela de tests entre contenedores CI.
Ver También
- Cypress Deep Dive: Arquitectura y Network Stubbing - Técnicas avanzadas después de dominar lo básico
- Cypress vs Selenium Comparación - Comparación detallada para elegir la herramienta correcta
- Playwright vs Cypress Comparación - Benchmarks y comparación de funciones
- Guía Completa de Playwright - El framework alternativo de Microsoft
- Tutorial de Selenium para Principiantes - El enfoque clásico de WebDriver
- Estrategia de Pirámide de Automatización - Dónde encaja Cypress en tu estrategia
- Tutorial de Automatización de Tests - Guía más amplia de automatización
- Gestión de Tests Flaky en CI/CD - Solucionar inestabilidad de tests
- GitHub Actions para Automatización QA - Patrones de integración CI/CD
- Visual AI Testing - Detectar regresiones visuales automáticamente
