Hola Cypress

En este post aprenderás a escribir tu primer test con Cypress, con solo un index.html verás que el testing es bello, sencillo y sumamente valioso dentro de tus proyectos. 💖

Miguel Segura

Miguel Segura

Hola Cypress

Cypress es un framework de testing que nos permite hacer pruebas End to End (E2E), que corren en el navegador y cuyo objetivo es replicar al máximo el comportamiento real de un usuario dentro de nuestra aplicación.

Cypress ya incluye librerías de aserciones, mocks y además tiene features muy interesantes como Screenshots y Videos de los test, intercepción de las peticiones http y otras tantas cosas que hacen que escribir test sea sencillo y rápido.

Nuestra aplicación

Para tener una aplicación que testear, vamos a crear un ejemplo sencillo solo con un index.html, cuyo única función será ingresar nuestro nombre y luego saludarnos.

Creemos la estructura básica que suele crearse automáticamente tecleando html:5 y después vamos a añadir nuestros inputs:

<div class="box">
  <input type="text" class="input is-primary is-large" placeholder="Introduce tu nombre" id="name-input" />
  <button class="button is-primary is-large is-fullwidth" id="name-btn">Saludame</button>
</div>

También le pondremos un poco de CSS porque somos frontends.

body {
  font-family: system-ui;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100vh;
}
button {
  margin-top: 16px;
  border: 1px solid transparent;
  border-radius: 4px;
  box-shadow: none;
  height: 2.5em;
  line-height: 1.5;
  background-color: #00d1b2;
  border-color: transparent;
  font-size: 1.5rem;
  color: #fff;
  width: 100%;
  outline: none;
}
input {
  border: 1px solid #00d1b2;
  line-height: 1.5rem;
  font-size: 1.5rem;
  box-shadow: inset 0 0.0625em 0.125em rgb(10 10 10 / 5%);
  color: #363636;
  height: 2.5em;
  padding-bottom: calc(.5em - 1px);
  padding-left: calc(.75em - 1px);
  padding-right: calc(.75em - 1px);
  padding-top: calc(.5em - 1px);
  outline: none;
  max-width: 100%;
  width: 100%;
  box-sizing: border-box;
  border-radius: 4px;
}
input:focus {
  box-shadow: 0 0 0 0.125em rgb(0 209 178 / 25%);
}
input::placeholder {
  color: #b3b3b3;
}
button:hover {
  background-color: #00c4a7;
}
button:active {
  background-color: #00b89c;
}
.box {
  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 0.5em 1em -0.125em rgb(10 10 10 / 10%), 0 0 0 1px rgb(10 10 10 / 2%);
  color: #4a4a4a;
  display: block;
  padding: 1.25rem;
}

Para tener un servidor de desarrollo, abriremos nuestro index.html utilizando la extensión de Live Server.

Nuestra aplicación que vamos a testearNuestra aplicación que vamos a testear

Ahora solo falta agregar un poco de JS para que cuando clickemos 'Saludame' apareza un mensaje de 'Bienvenido a Cypress ${tu nombre}'. :3

<script>
  const input = document.querySelector('#name-input')
  const button = document.querySelector('#name-btn')

  button.addEventListener('click', () => {
    const name = document.createElement('h1') 
    name.innerHTML = `Bienvenido a Cypress, ${input.value}` 
    name.classList.add('box')
    name.id = 'greeting'
    document.body.appendChild(name) 
  })
</script>

Instalando Cypres

Ya tenemos una aplicación sencilla, pero funcional, asi que vamos a instalar Cypress llendo a la terminal, inicializando un nuevo proyecto con yarn init o npm init e ingresando el comando.

yarn add cypress

Ten paciencia, puede tardar un poco, sobre todo si es la primera vez que usas Cypress en tu computador.

Una vez que añadimos Cypress a nuestro pequeño proyecto, se nos crean un par de carpetas dentro del mismo:

Archivos del proyecto despues de instalar CypressArchivos del proyecto despues de instalar Cypress

Cada una de ellas tiene un propósito y es el siguiente:

Fixtures: Aquí puedes guardar tus mocks que son la mejor manera de simular datos de respuesta para tu aplicación.

Integration: Todos los test que escribas van dentro de esta carpeta, vienen algunos ejemplos por default, los archivos llevaran el nombre en el formato nombre.spec.js.

Plugins: Aquí podemos darle poderes adicionales a Cypress para hacer cosas como agregar un Dark Mode o un tema a la interfaz de Cypress, añadir comandos y selectores más complejos, añadirle soporte a cosas como el Drag and Drop, etc.

Support : Si deseas crear comandos personalizados que hagan tareas repetitivas como el login y que estos estén disponibles en todos tus archivos de test, aqui debes colocarlos.

cypress.json: Contiene las configuraciones base de tu aplicación como el hostname o baseUrl, si deseas guardar screenshots de los errores o tomar video de toda tu ejecución, etc.

A continuación necesitamos añadir a nuestro package.json los scripts que usaremos para correr Cypress en modo de desarrollo y desde la consola:

"scripts": {
  "test:open": "cypress open",
  "test:run": "cypress run"
}

Tambien agregaremos un poco de configuracion dentro de cypress.json:

{
  // La direccion de donde esta corriendo nuestra aplicación
  "baseUrl": "http://localhost:5500",
  // EL tamaño del viewport que deseamos tener
  "viewportWidth": 1366,
  "viewportHeight": 768,
  "chromeWebSecurity": false
}

Y finalmente correremos el comando que abre la interfaz grafica de Cypress:

yarn test:open

¡Listo! Ya tienes Cypress corriendo en tu navegador.

Interfaz grafica de CypressInterfaz grafica de Cypress

Vamos a crear un archivo hello.spec.js dentro de nuestra carpeta integration donde vamos a escribir los test de nuestra aplicación, recuerda abrirlo en la interfaz así conforme avancemos y vayas guardando tus cambios, Cypress los tomara y los correrá.

La estructura básica de un test es la siguiente:

describe('Nombre de mi test', () => {
  // Grupo de aserciones que debe de cumplirse
  // para que el test sea considerado exitoso

  it('Nombre de la aserción', () => {
    // Comandos que  describen el estado deseado
    // de mis elementos y mi aplicación
  })

})

Siempre es recomendable detenerse a pensar un momento que queremos testear y cuáles son los pasos a realizar para verificar que esté funcionando correctamente. En nuestro caso para saber que nuestra aplicación nos está saludando correctamente necesitamos:

  • Poder entrar al sitio y que no nos arroje un error 404 o 500 o algo raro.
  • Que exista un input con el ID de name-input y un boton con el ID de name-btn y ningún saludo obviamente.
  • Que después de llenar nuestro input y darle click al botton aparezca un lindo saludo que lleve por ID gretting y que tenga por valor un texto que ya conocemos: Bienvenido a Cypress, Fulanito.

Teniendo esto en cuenta comencemos con nuestra estructura básica:

describe('La app me saluda correctamente', () => {

  it('Puedo visitar el sitio', () => {

  })

  it('El estado incial de mi app es el correcto', () => {

  })

  it('La app me saluda', () => {

  })

})

Listo, ahora vamos a escribir los comandos necesarios para verificar que estas aserciones son correctas.

Lo primero es entrar a nuestro sitio y para eso tenemos el comando visit que se usa de la siguiente manera:

cy.visit('http://localhost:3000')

// Va a la baseUrl configurada en cypress.json
cy.visit('/') 

// Va por el archivo local "index.html"
cy.visit('index.html') 

Ahora vamos a aplicarlo a nuestros test:

describe('La app me saluda correctamente', () => {

  it('Puedo visitar el sitio', () => {
    // Esto se rompera si nuestro server esta apagado o 
    // si hay un error 404 o similar
    cy.visit('/')
  })
 // ...
}

¡Listo! Ya has creado tu primer test exitoso.

Así debería lucir nuestra ventana con un test exitosoAsí debería lucir nuestra ventana con un test exitoso

Continuemos avanzando, ahora vamos a aprender a seleccionar un elemento del DOM usando Cypress utilizando el comando get que recibe por parámetros un selector que puede ser de clase, de ID, de atributo, de etiqueta o combinaciones entre estos:

// Selecciona todos los elementos de tipo input
cy.get('input')

// Selecciona los elementos con la clase .btn
cy.get('.btn')

// Selecciona el elemento con id #form
cy.get('#form')

// Selecciona el elemento que incluye la palabra 'blog' en su atributo href
cy.get('a[href*="blog"]')

// Selecciona el primer li dentro del elemento con la clase .menu
cy.get('.menu li:first')

// Selecciona el elemento con el atributo data-cy igual a price
// Esta es una muy buena practica por que las clases y los id's pueden cambiar, este es un atributo solo para cypress
cy.get('[data-cy=submit]')

// Si hay multiples elementos con el mismo selector puedes ir por el primero o el ultimo
cy.get('.item').first()
cy.get('.item').last()

// Existen muchos mas selectores interesantes

Una vez seleccionado nuestro elemento, podemos comenzar a realizar aserciones utilizando el comando should que recibe por parámetros cualquier descripción basada en asesiones de chai.

Veamos un poco de ejemplos, este concepto es sumamente intuitivo y autodescriptivo.

// El elemento nav debe ser visible
cy
  .get('nav')
  .should('be.visible')

// El elemento btn-sumary debe estar deshabilidado
cy
  .get('#btn-submit')
  .should('be.disabled')

// El formulario debe tener la clase form subscription
cy
   .get('form')
   .should('have.class', 'form-subscription')

// El contador debe tener un valor de 3
cy
   .get('counter')
   .should('have.value', 3)

Las posibilidades del should son infinitas, te recomiendo que revises la documentación a fondo, vamos a realizar las aserciones sobre el estado inicial de nuestra aplicación. Si deseas ver el HTML y el JS que generamos, vuelve arriba.

describe('La app me saluda correctamente', () => {
  // ...
  it('El estado incial de mi app es el correcto', () => {
    // Mis inputs se esta renderizando y es visible
    cy.get('#name-input')
      .should('be.visible')
    // Mis botton existe y es visible
    cy.get('#name-btn')
      .should('be.visible')

    // Mi saludo no deberia existir
    cy.get('#greeting')
      .should('not.exist')
  })
 // ...
})
Así luce mi navegador después de dos test exitososAsí luce mi navegador después de dos test exitosos

Ya sabemos como seleccionar elementos del DOM y como hacer aseciones sobre ellos, ahora solo nos faltan dos comandos mas: type y click para poder ejecutar la mismas acciones que el usuario.

Para usar .type() solo necesitas pasarle como parametro el string que sera ingresado y lucira asi:

cy.type('Cypress me esta escribiendo')cy.type('Cypress me esta escribiendo')

Y click(), solo lo ejecutas despues de seleccionar un elemento en el DOM y listo, agregemoslos a nuestro ulitmo test :3

describe('La app me saluda correctamente', () => {
  ...
  it('La app me saluda', () => {
    cy.get('#name-input')
      .type('miguelseguramx')
    cy.get('#name-btn')
      .click()
    cy.get('#greeting')
      .should('have.text', 'Bienvenido a Cypress, miguelseguramx')
  })

})

¡Listo! Lo has conseguido, has replicado el comportamiento de un humano con software y te has asegurado que un flujo va a correr exitosamente.

¡Maravilloso, ya tienes tu primera aplicación testeada!¡Maravilloso, ya tienes tu primera aplicación testeada!
Si por alguna razon los test o tu aplicación falla, Cypress te dara muy buenos logs para que comprendas que esta ocurriendo
alt

Una vez que esten exitosos puedes cerrar las ventanas de cypress y correr los test desde la terminal con yarn test:run

Cypress desde la terminalCypress desde la terminal

Ahora que ya conoces lo basico de Cypress, te invito a que continues revisando la documentacion y experimentes con nuestra aplicación para añadir nuevas funcionalidades y test. Si deseas puedes hacer la propia y conectarla a una API, incluso eres bienvenido a escribir la segunda parte de este blog si asi lo deseas.

Cuentame que te parecio el post y sobre que te gustaria que hablara en mis redes sociales o en Discord.

¡Se valiente y No te rIndas!

Ilustracion que representa como crece alguien profesionalmente

Entérate de las últimas novedades

Streamings, Noticias y Early Adopter bonus. Sé el primero en enterarte de todo.