Cypress, un framework de pruebas todo en uno

Cuando comenzamos un proyecto es normal que no sepamos con qué tecnologías desarrollar las pruebas unitarias o las pruebas e2e. En un entorno front, las decisiones son aún más complicadas por el gran número de librerías que existen.

Por ejemplo, para las pruebas unitarias tenemos a Mocha, Jasmine o Karma que pueden ser firmes candidatos. Además, los podemos acompañar de Chai como librería de aserciones (y que nos permite completar los tests).

Otras de las pruebas clave en nuestros proyectos son los mocks de datos. Una de las grandes alternativas es SinonJs, una librería potente y muy utilizada.

Dejo para el último lugar las e2e automáticas. En este punto suele aparecer habitualmente Selenium, con alguna envoltura más como Protractor o Nightwatch.

Pero, ¿no hay nada, en el mundo de los frameworks, que nos haga la vida más fácil? La respuesta, afortunadamente, es sí. ¿Quieres saber qué herramienta se va a convertir a partir de ahora en tu mejor amiga? ¡Sigue leyendo!

Cypress, la herramienta que engloba a todas las demás

Sí, Cypress es un framework “todo en uno” que incluye librerías de aserciones, de mocks y pruebas e2e automáticas sin utilizar Selenium.

Sí, has leído bien. Cypress no utiliza Selenium porque consta de una nueva arquitectura, construida desde cero, que ejecuta los comandos en el mismo ciclo de ejecución que la aplicación.

Detrás de Cypress se ejecuta un proceso Node que constantemente se comunica, sincroniza y ejecuta tareas, teniendo acceso tanto a la parte front como a la parte back de la aplicación y respondiendo a los eventos en tiempo real.

¿En qué tecnologías front-end se puede utilizar Cypress?

Esta herramienta está diseñada especialmente para manejar frameworks de JavaScript modernos, React, Angular, Vue, Elm, etc. Pero, también funciona igual de bien en páginas o aplicaciones renderizadas en servidor.

Como indican en su web:

“Cypress prueba todo lo que se ejecuta en un navegador web”.

Si se centra en pruebas e2e ¿cómo puede realizar pruebas unitarias?

Aunque su mayor cometido es realizar pruebas e2e, al ser un framework que incluye Mocha, Chai y Sinon se pueden realizar perfectamente pruebas unitarias.

Cypress opera dentro de la aplicación, lo que permite realizar las siguientes acciones, que son muy útiles, tanto para test unitarios como para los test e2e:

  • Realizar stubs de funciones para forzar ciertos comportamientos.
  • Testear las respuesta a errores modificando el status code de la respuesta del servidor a un 500.
  • Evitar hacer siempre login gracias a comandos como cy.request() que envía directamente una petición HTTP.

También tiene acceso nativo a todos los elementos, desde elementos DOM y funciones, hasta la ventana o temporizadores. Las pruebas tienen que estar escritas en Javascript.

Veamos un ejemplo

Instalación

Cypress se puede instalar de dos maneras: utilizando npm o descargándolo desde su página web.

Recomienda utilizar npm e instalarlo en el proyecto, de esta manera permite versionarlo como una dependencia más y es más fácil de incorporarlo en el proceso de Integración Continua.

Utilizando npm

Es tan sencillo como dirigirnos a la carpeta del proyecto y ejecutar el siguiente comando:

npm install cypress –save-dev

Abriendo Cypress

Si se ha utilizado el método de descarga, basta con hacer doble clic en el ejecutable.

Si se ha utilizado npm, como es el caso de este ejemplo, accedemos a la carpeta del proyecto y escribimos el siguiente comando por la consola:

cypress open

Al ejecutar el comando, se abrirá una ventana similar a la anterior pero ya situándonos en la carpeta del proyecto.

En la ventana podemos comprobar que tenemos dos tests: example_spec.js y poc_cypress.js, que son los ficheros de test que hay creados en el proyecto.

El fichero example_spec.js consta de una serie de ejemplos básicos que nos sirven de guia para realizar nuevos test y conocer sus funcionalidades.

Haciendo clic en example_specs.js Cypress ejecutará el navegador Google Chrome y empezará a pasar todos los test. Como podemos ver en la siguiente imagen, example_specs.js cuenta con 89 ejemplos.

A continuación vamos a explicar un par de de casos de prueba.

Casos de Prueba

Para que podáis descargar el código he subido a este repositorio de Git un proyecto llamado cypress-e2e. Los casos de prueba se encuentran dentro de la carpeta “cypressy dentro de la carpeta “integration.

Para realizar la automatización, necesitamos obtener los elementos del DOM que genera el navegador. Estos elementos se pueden obtener de diferentes maneras, a continuación se muestra un ejemplo de cómo obtenerlos vía CSS.

¿Cómo obtener elementos vía su identificador CSS?

La búsqueda de elementos vía CSS es bastante sencilla. Para explicar cómo obtener los elementos que utilizaremos en el ejemplo, explicaremos a continuación cómo obtener el primero.

El elemento que vamos a buscar es la lupa donde desplegamos el buscador.

Hacemos clic con el botón derecho del ratón y luego seleccionamos “inspeccionar”. Se abrirá la consola de Chrome y nos mostrará las características del elemento.

Como podemos ver en la imagen anterior, la lupa es el siguiente elemento:

<a class=”trigger” href=”#”>

Para la automatización es necesario identificar el elemento de manera unívoca. Para asegurarnos de que el elemento “a” con class “trigger” es el único elemento con estas propiedades, vamos a situarnos en la consola de Chrome e introducimos la siguiente función: $$(“a.trigger”).

Como podemos ver en la documentación de la consola de Chrome $$ es un alias de la función document.querySelectorAll().

Esta función muestra un conjunto de todos los elementos que coinciden con el selector de CSS especificado. En este caso el selector de CSS que queremos buscar es un link con la clase “trigger”.

Como podemos ver en la consola de Chrome, nos está indicando que en esta página solo hay un elemento “a” con el class “trigger”. Por lo que podemos concluir que el identificador CSS para el elemento de la lupa sería “a.trigger”.

Una vez que sabemos cómo buscar y obtener los identificadores de los elementos, vamos a explicar las principales funciones que se utilizan en el ejemplo.

Funciones de Cypress utilizadas en los ejemplos

En el primer ejemplo que veremos a continuación, se utilizan las siguientes funciones:

  • visit: redirige a Chrome a la url que se le pasa por parámetro.
  • get: obtiene un elemento por el identificador que le pasemos, para realizar acciones sobre él. Como hemos explicado en el apartado anterior, todos los identificadores que pasemos será obtenidos del CSS.
  • children: nos permite obtener un elemento que pasamos por parámetro, que desciende del elemento que hemos obtenido con la función get.
  • click: realiza un click sobre el elemento que hayamos obtenido con la función get.
  • type: escribe sobre el elemento obtenido un texto que pasamos por parámetro. Por ejemplo, usamos esta función para elementos input donde queremos introducir un texto.
  • submit: permite enviar el contenido del formulario.

A todas las funciones se les puede pasar un json con el elemento timeout. Este elemento nos permite incluir un tiempo que nos ayudará a esperar a que el elemento termine de cargar en la página.

Si no se incluye, Cypress intenta obtener los primeros elementos tras un visit y es muy probable que no dé tiempo a cargar el elemento o que no se haya cargado totalmente la estructura de la página.

Vamos a ejecutar los test que se encuentran en el fichero poc_cypress.js.

Primer ejemplo

En el primer ejemplo vamos a realizar una prueba automática que acceda a mi anterior post, que compruebe que el autor es “Nicolás Cordero” y que realice una búsqueda por la cadena de texto chai-HTTP para encontrar el post que he escrito.

Como podemos ver en el ejemplo, utilizamos Mocha para realizar el test y Chai como librería de aserciones.

describe('Accedo al blog de paradigma', function() {
   it('Busco algun blog de Nicolás', function() {
       cy.visit('https://www.paradigmadigital.com/dev/testeo-api-rest-mocha-chai-http/');
       cy.get('p.post-author',{ timeout: 2000 })
.children('a.author')
.should('contain', 'Nicolás Cordero');
       cy.get('a.trigger').click();
       cy.get('input.text').type('chai-HTTP');
       cy.get('form.inputWithButton').submit();
       cy.get('article.post_search_list',{ timeout: 5000 })
.children('h2')
.children('a')
.should('contain','Testeo de API REST con Mocha y Chai-HTTP');
       cy.get('article.post_search_list',{ timeout: 5000 })
.children('h2')
.children('a')
.should('have.attr', 'title')
.and('include', 'Testeo de API REST con Mocha y Chai-HTTP');
   })
})

En este ejemplo podemos observar que hemos pasado la variable timeout a ciertas funciones get, lo cual nos permite indicar un tiempo máximo que debe esperar hasta que el elemento ha sido cargado en la página.

Cypress, junto con la librería de aserciones Chai, nos permite realizar comprobaciones más completas de los elementos de una manera muy simple.

Por ejemplo, permite comprobar si el elemento dispone de algún atributo o si ese atributo o el elemento en cuestión contiene algún texto. En este ejemplo hemos usado:

  • contains: para indicar el contenido del elemento.
  • have.attr: para indicar que el elemento tiene un atributo en concreto.
  • include: para indicar que el atributo de un elemento incluye un texto.

Segundo ejemplo

En el segundo ejemplo vamos a utilizar la función cy.request, que nos va a permitir hacer una petición HTTP que realice la autenticación. De esta manera vamos a autenticarnos en la aplicación directamente sin tener que estar rellenando el formulario de login por cada prueba que hagamos.

Para realizar esta prueba voy a usar mi usuario de la página del diario deportivo As.

En la primera parte del código, vamos a intentar acceder a la página del perfil de un usuario registrado, pero nos redirecciona a la página de login. Para asegurarnos de que estamos en la página del login obtenemos el campo del formulario de login del email.

describe('Realizo un request', function(){
   it('si accedo a mi perfil sin estar registrado, me redirecciona al login', function(){

cy.visit('https://asfan.as.com/perfil/?backURL=https%3A%2F%2Fas.com%2F%3Fevent_log%3Dokdesc%26prod%3DREG&v=pf')
       //Campo de email del formulario de la pagina del login
       cy.get('#Email1')
   })

En la segunda parte del ejemplo, vamos a utilizar la función cy.request, que ejecuta una petición POST sobre la URL donde nos autenticamos.

Para ello, completamos el body con los datos del formulario e indicamos que es de tipo formulario. Acto seguido comprobamos que recibimos un código de estado 200 y, por último, accedemos a la página del perfil y comprobamos que aparece el elemento de la imagen de usuario.

Para utilizar este ejemplo hay que cambiar los campos user y password con los propios.

it('Hago el request del login', function(){
   var resp=cy.request({
       method:"POST",
       url: 'https://asfan.as.com/conectar',
       form: true,
       body: {
           Email1: "user",
           Password:"password",
           send:"1"
       }
   }).then((resp)=> {
       expect(resp.status).to.eq(200)
   })

   cy.visit('https://asfan.as.com/perfil/?backURL=https%3A%2F%2Fas.com%2F%3Fevent_log%3Dokdesc%26prod%3DREG&amp;v=pf')
   cy.get("div.ug_foto")

})

Tercer Ejemplo

El tercer ejemplo es muy parecido al anterior sólo que es un poco más complejo. Vamos a utilizar la página de GitHub y vamos a realizar una autenticación con una petición cy.request. La peculiaridad de este ejemplo, es que GitHub dispone de un token oculto en el documento HTML que hay que extraer para realizar la autenticación.

Adicionalmente en este ejemplo, vamos a utilizar “Cypress.Commands.add”, que nos va a permitir envolver una función Javascript en un comando de Cypress, para poder utilizarlo cuando queramos.

Como podemos observar en el siguiente fragmento de código, vamos a añadir el comando loginByToken al que se le va a pasar un token. 

Después se realiza un cy.request, que va a realizar una petición POST sobre la URL de Github que efectúa la autenticación. Se añaden las cabeceras y se rellena el cuerpo de la petición con el usuario, la password y el token que se ha recibido como parámetro.

Cypress.Commands.add('loginByToken', (token) => {
   cy.request({
   method: 'POST',
   url: 'https://github.com/session',
   gzip: true,
   form: true,
   body: {
       authenticity_token: token,
       login: 'user',
       password: 'pass'
   }
   })
})

Una vez realizado el comando loginByToken, vamos a explicar la prueba que permite la autenticación en GitHub.

Primero necesitamos pasarle el token de autenticación llamado authenticity_token, que se encuentra oculto en el formulario de autenticación de la página web de login.

Para conseguir el token, realizamos un request sobre la URL de login de Github que nos proporcionará el body de la página.

describe('Realizo un request', function(){
   it('Hago el request de la buqueda', function(){
       cy.request('https://github.com/login')
           .its('body')
           .then((body) => {

Una vez obtenido el body, utilizamos JQuery para poder realizar consultas de elementos del documento HTML. Cypress incluye JQuery y se expone de la siguiente manera: Cypress.$().

A esta función le pasamos el body, realizamos una consulta del elemento input con nombre authenticity_token y obtenemos su valor.

const $html = Cypress.$(body);
const token  = $html.find("input[name=authenticity_token]").val()

A continuación, utilizamos el comando que hemos creado, cy.loginByToken, pasándole como parámetro el token capturado y comprobamos que la autenticación ha salido correctamente comprobando que hemos obtenido un código de estado 200.

cy.loginByToken(token).then((resp) => {
            expect(resp.status).to.eq(200)
                       })

Finalmente, accedemos a la página del perfil y comprobamos que existe la imagen de avatar.

cy.visit('https://github.com/settings/profile')
       cy.get("img.avatar",{timeout:3000})
   })
})

A continuación vamos a ejecutar el ejemplo que hemos realizado para ver los reportes que genera Cypress.

Ejecución del ejemplo

Cuando ejecutamos el test desde Cypress, podemos observar, en la siguiente imagen, cómo va ejecutando todos los pasos, las peticiones HTTP que estas acciones generan, el tiempo de ejecución de las pruebas, número de pruebas pasadas y fallidas y los asserts que hemos incluido en las pruebas.

Otra característica muy útil que ofrece el reporte de pruebas de Cypress es que si nos situamos sobre una acción concreta nos muestra una captura de pantalla de esa acción.

Por ejemplo, el paso 7, cuando escribe en el buscador “chai-HTTP”, Cypress nos ofrece una captura de pantalla del momento en el que realiza la acción.

¿Qué funcionalidades tiene?

Entre las muchas funcionalidades que ofrece Cypress, una de ellas es que puede ejecutar las pruebas sin la necesidad de tener un navegador instalado. 

El navegador headless y tiene muchas ventajas:

  • Es más rápido, ya que corre en memoria y no requiere de toda la preparación que tienen los navegadores gráficos.
  • Se pueden tomar screenshots o vídeos como si fuera una prueba automatizada normal.
  • Cuando se trabaja con una herramienta de integración continua y entrega continua, como Jenkins, nos permite correr las pruebas e2e sin necesidad de tener un navegador instalado.

Cuando ejecutamos el comando cypress run desde la carpeta el proyecto, ejecuta los test de forma headlesslyPor defecto, Cypress ejecuta todos los test desde la consola y graba un vídeo con las pruebas realizadas.

Como podemos ver en la salida del comando, ha grabado un vídeo dentro de la carpeta “videos” llamado vjuix.mp4 que podemos ver a continuación:

Reportes Personalizados

Ya que el reporte que aparece por consola no es muy legible en esta versión, Cypress permite añadir otros reportes de pruebas diferentes. En este proyecto de ejemplo hemos añadido el reporte de pruebas mochawesome.

Se ha instalado la dependencia y hemos incluido la siguiente línea en el fichero de configuración cypress.json:

"reporter": "mochawesome"

Ejecutamos el siguiente comando por la consola:

cypress run --reporter mochawesome

En la carpeta mochawesome-report nos genera un reporte de pruebas como el que podemos ver en la siguiente imagen:

Conclusión

Cypress es una herramienta muy completa para realizar pruebas end to end automáticas sobre un frontal web. Ofrece una API muy práctica y el fichero example_spec.js contiene muchos ejemplos de las funciones.

Personalmente, he usado otras herramientas de automatización como Protractor y Selenium y he de reconocer que Cypress me ha sorprendido gratamente por la velocidad y sencillez con la que se ejecutan los test, y por todas las funcionalidades que incluye.

Es una herramienta a tener en cuenta en futuros proyectos y os animo a todos a probarla.

Ingeniero informático desde 2012. Pegándome con bugs desde 2013 (ง'̀-'́)ง Miembro del equipo de QAradigma. Gran aficionado a los videojuegos desde que tengo memoria y apasionado de los ordenadores desde que a mi hermano le regalaron un 486.

Ver toda la actividad de Nicolás Cordero

Escribe un comentario