Escribiendo el primer test

Para empezar a escribir los primeros tests con Cypress.io es necesario utilizar cualquier editor de código favorito. Para esta práctica nosotros vamos a utilizar Visual Studio Code.

Ahora, vamos a ver que la sintaxis para escribir los tests en Cypress es bastante sencilla. Lo primero que vamos hacer es crear un fichero Javascript dentro de la carpeta cypress/integration que será el archivo donde escribiremos el código para los tests.

  1. Crear un archivo llamado todomvc.spec.js en la carpeta cypress/integration

Cypress también permite archivos tipo: .js - .jsx - .coffee - .cjsx

2. La aplicación que vamos a testear es una página para crear lista de Tareas "similar" a Microsoft To Do. http://todomvc-app-for-testing.surge.sh

3. Ahora vamos a escribir nuestros casos de pruebas con Cypress.

describe('Escenario de Prueba: Crear tareas en la TODO App para validar su funcionamiento', () => {

    it('Validar el acceso a la TODO App', () => {
        cy.visit('http://todomvc-app-for-testing.surge.sh')
        expect(true).to.equal(true)
    });

    it('Agregar nuevas tareas en la TODO App', () => {
        cy.get('.new-todo').type('Escribir ensayo para la universidad').type('{enter}')
        cy.get('.new-todo').type('Comprar comida para el gato').type('{enter}')
        cy.get('.new-todo').type('Realizar slides para la conferencia').type('{enter}')
        cy.get('.new-todo').type('Llamar al odontólogo para programar cita').type('{enter}')
    });

    it('Marcar tareas como completadas en la TODO App', () => {
        cy.get(':nth-child(3) > .view > .toggle').click()
        cy.get(':nth-child(1) > .view > .toggle').click()
        cy.contains('Clear completed')
        cy.get('.clear-completed').should('have.text', 'Clear completed')    
    });

    it('Realizar validaciones (Assertions) en la TODO App', () => {
        cy.get(':nth-child(2) > .view > .toggle').should('not.be.checked')
        cy.get(':nth-child(1) > a').should('be.visible')
        cy.get(':nth-child(2) > a').should('be.visible')
        cy.get(':nth-child(3) > a').should('be.visible')
        cy.get('label').should('have.css', 'padding')
        cy.get(':nth-child(2) > .view > label').should('have.text', 'Realizar slides para la conferencia')
        cy.get(':nth-child(2) > .view > label').should('not.be.checked')
        cy.get('.completed > .view > label').should('have.css', 'text-decoration-line', 'line-through')
    });
    
});

Cómo podemos observar en el código, el describe('Escenario de Prueba') plantea 4 casos de pruebas a través de los métodos it(). En Cypress la sintaxis es muy sencilla de leer para cualquier equipo multifuncional, por lo que fácilmente las pruebas End-to-End puede lograr que el equipo tenga una comprensión común de qué probar de forma eficiente y rápida sin grandes conocimientos técnicos.

Comandos de Cypress utilizados en el ejemplo

En el primer código de ejemplo se utilizaron los siguientes comandos.

describe(): Se permite darle una descripción a nuestro escenario de pruebas. it(): Se define un test a través de una función con un callback. Nota: Todos los comandos de Cypress comienza con cy. cy.visit: Método para redirigir el Chrome Browser a la URL que se le pasa por parámetro. cy.get: Obtiene un elemento por el identificador que le pasemos para realizar acciones sobre él. Cypress acepta los selectores por ID o CSS. cy.contains: Obtiene del elemento DOM una cadena de texto. Los elementos DOM pueden contener más de un texto deseado y aún así coincidir. .should: Comando utilizado para crear una afirmación (Assertions). Las afirmaciones se reintentan automáticamente hasta que pasan o caducan. .type: Escribe sobre el elemento obtenido un texto o realiza una acción que pasamos por parámetro. Por ejemplo, usamos esta función para elementos input donde queremos introducir un texto. click(): Realiza un click sobre el elemento que hayamos obtenido con la función get.

Interactuando con elementos

Cypress es un framekwork para testing relativamente nuevo, por lo que todavía hay algunos errores y características faltantes. Sin embargo, generalmente tiene unos features muy interesantes que nos permite automatizar de manera rápida pruebas End-to-End. Una de esas características es la facilidad con la que se pueden capturar elementos para automatizar. Veamos algunas de estas opciones:

  • Open selector playground Se encuentra ubicado en la parte superior del browser de ejecución, esta herramienta nos permite capturar cualquier elemento de la página que estamos testeando y facilmente nos trae los selectores por ID o por las clases CSS de los elementos DOM.

  • Unicidad Cypress calculará automáticamente un selector único para usar sobre el elemento objetivo ejecutando una serie de estrategias de selectores. Por defecto, Cypress favorecerá los siguientes tipos:

    data-cy data-test data-testid id class tag attributes nth-child

Mejores prácticas

La mayoría de los frameworks de testing (como Selenium) funcionan ejecutándose fuera del navegador y ejecutando comandos remotos a través de la red. Cypress en su lugar se ejecuta en el mismo bucle que su aplicación. Esto permite el acceso nativo a todos los objetos: window, document, un elemento DOM, la instancia de la aplicación, etc.

Cypress te permite actuar siempre como un usuario final (End-to-End) para generar el estado de una situación dada mediante la programación de datos estáticos y controlar ciertas variables de entorno.

  • cy.visit: Este comando visitara una URL específica el cual le permite interactuar con los elementos de una página web. La mejor práctica para este comando es definir una variable baseUrl en el archivo fixtures/example.json el cual te permite omitir tener que definir una URL dentro del método. Al configurar una baseUrl también se puede evitar perder un tiempo precioso cuando las pruebas se ejecutan, porque Cypress cargará la ventana principal directamente en la baseUrl.

  • cy.get / cy.contains: Estos dos comandos son la columna vertebral de cualquier pruebas de UI en Cypress. Sirven para dos propósitos principales:

    • 1. Para verificar si cierto elemento o texto se muestra correctamente.

    • 2. Para encadenar una acción o aserción del elemento especificado.

    Idealmente, los elementos deben seleccionarse en función de los atributos data- * ya que las clases y los ID pueden cambiar dinámicamente. Sin embargo, si esto no es posible, solo asegúrese de que el selector que elija usar (clase, id, etc.) no sea susceptible de cambio. Por ejemplo, para un botón que se ve así en el código:

<button id="main" class="btn btn-large" data-cy="submit">
    Submit
</button>

La mejor práctica a utiliza podría ser:

cy.get('[data-cy=submit]’').click()

// OR

cy.contains('Submit').click()
  • cy.request: Otra característica de Cypress es que permite accesos directos a través de ciertos comandos para realizar solicitudes HTTP que en realidad parecerán provenir del navegador. Este comando cy.request se puede utilizar principalmente para generar el estado de un usuario conectado. Puede obtener y configurar cookies automáticamente, lo que le permite omitir por completo el tiempo perdido utilizando siempre la interfaz de usuario para iniciar sesión.

cy.request({
  method: 'POST',
  url: '/login',
  form: true,
  body: {
    username: 'pataconf',
    password: 'password2019'
  }
})
cy.getCookie('cypress-session-cookie').should('exist')

En el ejemplo anterior, se envía una solicitud POST para el endpoint de /login y, a través de esto, la cypress-session-cookie se configurará automáticamente en el navegador, lo que permite alcanzar el inicio de sesión.

Una buena práctica sería envolver este cy.request en un comando personalizado de Cypress y colocarlo en el archivo cypress / support / command.js para que pueda reutilizarse en todas las pruebas. Como se muestra abajo:

Cypress.Commands.add('login', function () {
  cy.request({
    method: 'POST',
    url: '/login',
    form: true,
    body: {
      username: 'hbsmith',
      password: 'password123'
    }
  })
})
// Thus, cy.login can be used in all other files
  • iframe: Por el momento, Cypress no soporta nativamente interactuar con iframes o carga de archivos, sin embargo, existe una solución para trabajar con estos elementos. Entonces, en orden para seleccionar elementos dentro de un iframe, usted tendrá que hacer lo siguiente: cy.get el iframe que quieres apuntar cy.find el elemento dentro del contenido del iframe cy.wrap el elemento para encadenar los comandos de acción

cy.get('iframe').then($iframe => {
  const $body = $iframe.contents().find('body')  
  
  cy.wrap($body)
    .find('button')
    .click()
})
  • Calendarios: Otro de los elementos más importantes son los calendarios, a pesar de que Cypress tampoco lo soporta nativamente, existe una opción válida para trabajar con los calendarios:

cy.iframe('[title="Calendar"]').as('calenderIframe') // create alias

cy.get('@calenderIframe') // use alias to efficiently chain commands
      .find('.Calendar__day')
      .eq(0)
      .click()
      
cy.get('@calenderIframe')
      .find('.Calendar__day')
      .eq(1)
      .click()
      
cy.get('@calenderIframe')
      .find('.Calendar__day')
      .eq(2)
      .click()
  • Carga de archivos: Otro de las características no soportadas por Cypress de forma nativa, sin embargo, hay una opción valida para realizar la carga de un archivo dentro de nuestros tests. Lo primero es que el archivo que vamos a cargar debe estar dentro de la carpeta fixtures/image.png, luego a través del comando cy.fixture cargaremos la imagen. El siguiente código es un ejemplo para cargar un archivo con Cypress:

cy.fixture('images/image.png').as('image')
      
cy.get('input[type=file]').then($input => {
        return Cypress.Blob.base64StringToBlob(this.image,
        'image/png').then(
          blob => {
            const imageFile = new File([blob], 'image.png', { type:
            'image/png' })
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(imageFile)
            $input[0].files = dataTransfer.files
          }
        )
      })

Otros comandos comunes utilizados en Cypress

Comandos para controlar el Browser cy.visit('/') - Carga una URL específica cy.reload() - Recarga la URL actual cy.go('back') - Navega una página atrás o adelante Comandos para seleccionar elementos del DOM cy.get('.selector') - Acceder a un elemento especifico cy.contains('texto') - Acceder a un elemento por un texto especifico cy.contains('.selector', 'texto') - Acceder a un elemento por ambos contenido y selector Comandos para interactuar con elementos del DOM cy.get('.selector').click() - Hace click sobre un elemento cy.get('.selector').dblclick() - Hace doble click sobre un elemento cy.get('input').type() - Escribe texto de un input cy.get('input').clear() - Limpia el texto de un input cy.get('checkbox').check() - Hace check en un elemento tipo checkbox cy.get('checkbox').uncheck() - Quita el check en un elemento tipo checkbox cy.get('select').select('item') - Selecciona un ítem de un elemento

Cypress también incluye los Hooks que son funciones o métodos que se ejecutan en determinados momentos del flujo de ejecución de los tests.

before(): Función que se ejecuta una vez antes de la ejecución de todos los tests del mismo grupo. beforeEach(): Función que se ejecuta antes de cada test individual. afterEach(): Función que se ejecuta después de cada test individual. after(): Función que se ejecuta una vez, al finalizar la ejecución de todos los tests del mismo grupo. it.skip(): Se omite el test durante la ejecución.

Si tienes alguna duda, puedes visitar el código en Github: https://github.com/fugazi/CypressPataconf

Last updated