E2E Testing con Puppeteer

Published on
5 minutos

Puppeteer, suena divertido, pero... ¿No había otro nombre más fácil de escribir y/o de pronunciar en inglés? Gracias Google.

Llevo tiempo mirando diferentes formas de hacer E2E tests y de entre todas las opciones me he empezado a pegar con Puppeteer.

Getting into Puppeteer : Inject | Interact | Keys | Capture | Select |  Hacker Noon

Desde hace años el mundo de los test funcionales ha sido sinónimo de Selenium, siendo open source y con posibilidad de integrarlo con diferentes tecnologías como .Net, Python, Node.js además de Java hacía de Selenium un caballo ganador pero desde hace un tiempo están saliendo nuevas opciones que le están ganando terreno a gran velocidad: Puppeteer, Cypress, TestCafe....

Puppeteer es una librería de Node.js que ofrece una API de alto nivel que permite automatizar acciones sobre Google Chrome y Chromium. En poco tiempo se podrá usar Puppeteer con Firefox también (por el momento está en preview). Si en tu caso no necesitas hacer tests funcionales cross-browser puede ser una buena opción siempre y cuando sepas algo de JavaScript.

La API que ofrece Puppeteer controla el navegador a través del protocolo DevTools lo que hace tener un 'mayor' control que Selenium, además permite acceder a la medición de tiempos de carga y renderizado a través de la herramienta Performace Analysis de Chrome. Por cierto, está desarrollada y mantenida por Google ¿Suena bien? ¿Suena fancy? Genial, pero lo importante no es que suene bien, es que funcione bien. :)

Después de saber un poco sobre el Puppeteer, vamos al lio, desarrollemos nuestro primer noob test.

Lo primero de todo es definir las dependencias que va a tener nuestro proyecto en el package.json, en mi caso he optado por Mocha y Chai ya que estoy más familiarizado con ellos que con otros. Mocha es un test framework que nos ayudará a escribir nuestros test y Chai una librería de aserciones que nos permitirá comprobar si los resultados son los esperados. Por aquí vemos como quedaría nuestro package.json.

<code lang="json" class="language-json line-numbers">"devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^8.2.1",
    "puppeteer": "^5.5.0"
}

El test que vamos a desarrollar es el siguiente: Abrir el navegador, navegar hasta el blog y una vez en la página comprobar que el título de la página es correcto. Sencillo ¿no?, veamos un poco de código:

<code lang="javascript" class="language-javascript">const puppeteer = require("puppeteer");
const { expect } = require("chai");

describe("E2E Testing", () => {
  let browser;
  let page;

  beforeEach(async () => {
    browser = await puppeteer.launch({
      headless: false,
      defaultViewport: null,
      width: 1920,
      height: 1080,
      args: ["--disable-dev-shm-usage"],
    });
    page = await browser.newPage();
    await page.goto("https://iortizdezarate.com/", {
      waitUntil: "load",
      timeout: 0,
    });
  });

  afterEach(async () => {
    await browser.close();
  });

Leyendo el código, 'importamos' las dependencias (Puppeteer y Chai) para poder usarlas, definimos el escenario (E2E Testing) y vemos dos funciones (beforeEach y afterEach) que se ejecutarán cada vez en cada test. En nuestro caso hemos decidido que por cada test, se abra un nuevo navegador que nos redirija a la HOME del blog, y espere que se cargue. Además una vez finalizado el test, el navegador se cerrará.

Ya tenemos el escenario, ahora vayamos con el primer test:

<code lang="javascript" class="language-javascript">  it("My website is up", async () => {
    expect(await page.title()).to.eql(
      "Israel Ortiz de Zárate - Blog personal",
      { waitUntil: "domcontentloaded" }
    );

El test es bastante sencillo, como hemos comentado antes, la función beforeEach nos redirigirá a la página principal del blog y en nuestro test comprobaremos que el título de la página es "Israel Ortiz de Zárate - Blog personal" con eso determinaremos que la página web está levantada o al menos la cabecera ha cargado. ¿Dónde buscamos el valor del page.title()?

Para encontrar este valor, debemos buscar en el código de la página, y más concretamente en la parte <head>, buscamos la etiqueta <title>. Este proceso es el que realizará nuestro test de forma automática.

thumbnail-image

¡Perfecto! Ya tenemos todo, nuestro archivo main.js nos habrá quedado así

<code lang="javascript" class="language-javascript">const puppeteer = require("puppeteer");
const { expect } = require("chai");

describe("E2E Testing", () => {
  let browser;
  let page;

  beforeEach(async () => {
    browser = await puppeteer.launch({
      headless: false,
      defaultViewport: null,
      width: 1920,
      height: 1080,
      args: ["--disable-dev-shm-usage"],
    });
    page = await browser.newPage();
    await page.goto("https://iortizdezarate.com/", {
      waitUntil: "load",
      timeout: 0,
    });
  });

  afterEach(async () => {
    await browser.close();
  });

  it("My website is up", async () => {
    expect(await page.title()).to.eql(
      "Israel Ortiz de Zárate - Blog personal",
      { waitUntil: "domcontentloaded" }
    );
  });
});

Ya solo nos falta ejecutar el test con npm test (o como lo tengamos configurado en nuestro package.json) y esperar el resultado. Si os fijáis en el código, estamos usando la opción headless: false lo que nos permite ver el navegador cuando ejecutamos nuestros test, si cambiamos esa variable a true, no veremos la ejecución, pero se estará ejecutando.

Una vez pasado el test, podremos ver por consola el resultado, si ha pasado correctamente veremos el siguiente mensaje:

thumbnail-image

En caso de que no haya pasado correctamente veremos otro mensaje diferente con los valores que esperaba encontrar y los que ha encontrado y nos saltará un error:

thumbnail-image

Esto solo es un primer paso para ver el funcionamiento de Puppeteer en tests funcionales, las posibilidades son enormes y combinado con otras librerías como Pixelmatch y Testing Library puedes conseguir tener una suite de test muy completa que cubra gran parte de las funcionalidades de tu web. Además de test, con Puppeteer se pueden hacer cosas interesantes como scrapping web, automatización de tareas web....

Espero que os haya gustado el post, si veo que tiene 'movimiento' es posible que haga una segunda parte haciendo test con comparación de imágenes en base a pixeles con PixelMatch...

Si te ha gustado, comparte, si tienes alguna pregunta, deja un comentario.

¡Saludos!

BONUS TRACK

Hace un tiempo vi la esta charla de Ángel M. de Miguel en el Sevilla JS en la que habla de sus aventuras con Puppeteer y procesos automatizados en la web y te hace ver la potencialidad de esta herramienta. Si tenéis algo de tiempo echadle un vistazo.