¿Qué es Cypress?
Cypress es una herramienta de pruebas end-to-end que se ejecuta dentro del navegador junto a tu aplicación. Esta decisión arquitectónica le otorga una experiencia de desarrollador excepcionalmente buena: un runner interactivo donde puedes ver la ejecución de los comandos, la capacidad de inspeccionar el DOM en cada paso y un depurador de “viaje en el tiempo” que reproduce cada acción.
Es una de las herramientas de pruebas de navegador más populares y es especialmente apreciada por su facilidad de uso. Los comandos se encolan automáticamente, las aserciones se reintentan hasta que pasan y el runner deja muy claro qué sucedió cuando una prueba falla.
Escribiendo una prueba
Las pruebas de Cypress utilizan una API de comandos encadenables y una estructura describe/it.
// cypress/e2e/todos.cy.js
describe("todos", () => {
beforeEach(() => {
cy.visit("/todos");
});
it("adds a todo", () => {
cy.get('[data-cy="new-todo"]').type("Write tests");
cy.get('[data-cy="add"]').click();
cy.get('[data-cy="todo"]').should("have.length", 1);
cy.contains("Write tests").should("be.visible");
});
});
Los comandos de Cypress se encolan y se ejecutan en orden, aunque retornen inmediatamente. Es por eso que debes encadenar .then o usar alias en lugar de asignar el resultado de un comando a una variable.
Comandos y aserciones
Los comandos principales cubren la navegación, las consultas y la interacción:
cy.visit(url)abre una página.cy.get(selector)realiza consultas mediante selectores CSS.cy.contains(text)busca un elemento por su texto..type(),.click(),.select(),.check()interactúan con los elementos..should()realiza aserciones y reintentos.
// assertions.cy.js
cy.get("form").should("be.visible");
cy.get('[data-cy="count"]').should("have.text", "3");
cy.get("button").should("be.disabled");
cy.get('[data-cy="row"]').should("have.length", 5);
Debido a que .should() reintenta la operación hasta que se cumpla o se agote el tiempo de espera, rara vez es necesario esperar explícitamente. Este es el mismo enfoque basado en reintentos que hace que las pruebas de Playwright sean resilientes, aplicado a una API encadenada.
Stubbing de red con cy.intercept
cy.intercept es la forma de hacer que las pruebas sean deterministas. Permite hacer stub de respuestas, espiar el tráfico real, modificar peticiones y controlar los tiempos de respuesta.
// intercept.cy.js
cy.intercept("GET", "/api/users", {
statusCode: 500,
body: { message: "Server error" },
}).as("users");
cy.visit("/users");
cy.wait("@users");
cy.contains("Something went wrong").should("be.visible");
El uso de un alias con cy.wait("@users") te permite realizar aserciones sobre la petición y esperar la respuesta sin tener que adivinar los tiempos de ejecución. Hacer stub de estados de error de esta manera es mucho más fiable que intentar provocarlos desde un backend real.
Comandos personalizados y fixtures
Los flujos que se repiten deben ir en un comando personalizado, lo que permite que los tests sean más legibles.
// cypress/support/commands.js
Cypress.Commands.add("login", (email, password) => {
cy.visit("/login");
cy.get('[data-cy="email"]').type(email);
cy.get('[data-cy="password"]').type(password);
cy.get('[data-cy="submit"]').click();
});
// usage
cy.login("[email protected]", "secret");
Los fixtures cargan datos estáticos desde cypress/fixtures, y cy.fixture("users.json").then(...) proporciona entradas predecibles para los tests. Combinados con cy.intercept, los fixtures son la forma estándar de testear sin un backend activo.
Selección de elementos
Cypress funciona con selectores CSS, por lo que la tentación de vincular las pruebas al estilo es real. Evítalo. Prioriza, en este orden:
- Consultas accesibles a través de
@testing-library/cypress(findByRole,findByLabelText). cy.containspara texto visible.- Un atributo
data-cydedicado cuando no haya una identidad visible para el usuario.
Un atributo data-cy es explícito y estable, por lo que las pruebas sobreviven a los rediseños. Los selectores basados en clases no lo hacen.
Dónde encaja Cypress
Cypress es la capa de end-to-end. Mantén la pirámide equilibrada: tests unitarios rápidos con Vitest o Jest, tests de componentes con Testing Library, y un conjunto enfocado de flujos end-to-end en Cypress o Playwright. Desplaza los casos borde hacia las capas más económicas y reserva el e2e para los caminos que deben funcionar de principio a fin.
Mejores prácticas
- Simula la red con
cy.interceptpara obtener pruebas deterministas. - Utiliza
data-cyo consultas accesibles en lugar de selectores de estilo. - Mantén las pruebas independientes para que puedan ejecutarse en paralelo.
- Encapsula los flujos repetitivos en comandos personalizados.
- Inicia sesión a través de un comando personalizado o una llamada a la API en lugar de usar la interfaz de usuario en cada ocasión.
- Realiza aserciones sobre los resultados visibles para el usuario, no sobre el estado interno.
- Ejecuta en modo headless en CI y reserva el ejecutor interactivo para la depuración local.
Errores comunes
- Esperar con
cy.wait(milliseconds)en lugar de usar un alias de red o una aserción. - Acoplar los selectores a clases de CSS y a la estructura.
- Compartir el estado entre tests, lo que provoca fallos dependientes del orden de ejecución.
- Intentar almacenar los resultados de los comandos en variables sin usar
.theno aliases. - Probar todo de extremo a extremo (end-to-end) en lugar de hacerlo en la capa adecuada.
- Iniciar sesión a través de la UI antes de cada test, ralentizando la suite de pruebas.
Próximos pasos
Cypress es una herramienta amigable y potente para probar flujos reales de usuario. Compárala con Playwright si buscas una alternativa multiplataforma, y construye las capas inferiores con Vitest y Testing Library. Después, añade un comando de login personalizado y un flujo crítico, y haz crecer tu suite de pruebas a partir de ahí.