Siete meses después de la primera entrega, vuelvo a la carga con la segunda parte del cómo hacer E2E Testing con Puppeteer. Tras ese primer post en el que veíamos los conceptos básicos que nos permite hacer Puppeteer, hoy vamos a añadir a nuestro proyecto la posibilidad de hacer comparación de imágenes, jugar con umbrales de tolerancia y ver los resultados de una forma rápida y visual.
Comparar imágenes no suele ser buena idea
Por definición, hacer test funcionales en base a comparación de imágenes no suele un buena idea o al menos no debe ser la primera opción. La tarjeta gráfica, el sistema operativo, además de las dimensiones o la resolución son factores a tener en muy en cuenta. Cuanto mayores sean las dimensiones de las capturas que estás haciendo, mayor es la posibilidad de que tus test sean menos robustos. Pero a pesar de todo, en ocasiones puede que sea la única solución.
El concepto de este tipo de test es simple. Realizas unas capturas master, que serán las capturas válidas con las que cada test probará si su imagen es correcta. Un ejemplo simple podría ser una formulario de contacto que tiene n campos para rellenar: nombre, apellidos, DNI y teléfono. Hacemos una captura de esa ventana y comprobaremos cada vez que esa ventana es exactamente igual. En caso de cambiar (añadir campo dirección), nuestro test deberá fallar.
Manos a la obra
¿Qué necesitamos para hacerlo? En este caso vamos a optar por añadir una nueva librería a nuestro proyecto dentro del package.json, la librería en cuestión es PixelMatch. Ellos la definen así: "The smallest, simplest and fastest JavaScript pixel-level image comparison library", PixelMatch es propiedad de Mapbox y tiene licencia ISC lo cual nos permite usarla libremente.
Para usar PixelMatch en nuestro proyecto, debemos añadir el paquete npm a nuestro package.json. Actualmente PixelMatch está en la versión 5.2.1 así que la añadiremos de esta forma:
"dependencies": {
...
"pixelmatch": "5.2.1",
...
},
El siguiente paso será hacer las capturas master, las correctas, para ello usaremos la función page.screenshot y guardaremos la imagen en la raíz.
await page.screenshot({path: 'master-screenshot.png'});
Y por último, solo nos quedaría desarrollar el test funcional. Debe realizar la captura, guardar en constantes la información de las imágenes y hacer la comparación con la captura master realizada anteriormente. Así sería el código:
await page.screenshot({ path: "current-screenshot.png" });
const img1 = PNG.sync.read(fs.readFileSync("current-screenshot.png"));
const img2 = PNG.sync.read(fs.readFileSync("master-screenshot.png"));
const diff = new PNG({ width: img1.width, height: img2.height });
const numDiffPixels = pixelmatch(
img1.data,
img2.data,
diff.data,
img1.width,
img1.height,
{ threshold: 0.1 }
);
fs.writeFileSync("diff.png", PNG.sync.write(diff));
El código está sacado de la documentación oficial y funciona perfectamente. Recoge en constantes las imágenes y genera una imagen con el nombre diff entrelazando ambas. Además, guarda la información de los pixeles diferentes en una constante con el nombre numDiffPixels.
Solo nos faltaría escribir unas aserciones para comprobar los resultados. Si las imágenes que estamos comparando son exactamente iguales, la constante numDiffPixels debe tener un valor de 0. Para crear esta aserción escribiríamos este código:
expect(numDiffPixels).equal(0);
Comprobando los resultados
Vaya bien o no el test, estamos creando siempre una imagen con nombre diff.png. Esta imagen nos ayudará a identificar visualmente los pixels que no son iguales. Veamos el resultado de dos imágenes diferentes:
Podemos ver que en las diferencias en color rojo. El problema es que ha desaparecido un tag del post. Podéis pasaros por esta página donde se comparan diferentes imágenes y te permite modificar la sensibilidad de las comparaciones.
Y esto ha sido todo, espero que te haya resultado interesante, si es así, compártelo.
Un saludo,