Escribí un generador de informes personalizados para Jest y lo
publiqué en
GitHub . Mi constructor se llama Jest-snapshots-book, crea un libro HTML de instantáneas de los componentes de una aplicación React.

El artículo discutirá qué es Jest, las pruebas de instantáneas, que generalmente requieren un generador de informes adicional y cómo escribirlas. Básicamente, todo esto se aplica a las pruebas de los componentes React, pero en teoría se puede aplicar cuando se trabaja con datos serializables.
Componente de reacción del paginador
Por ejemplo, en el artículo
probaremos el componente paginador (
Paginator ). Es parte de nuestro proyecto para crear aplicaciones sin servidor en AWS (
GitHub ). La tarea de dicho componente es mostrar botones para navegar por las páginas de una tabla u otra cosa.
Este es un componente funcional simple sin componente sin estado (componente sin estado). Como entrada, recibe de los accesorios el número total de páginas, la página actual y la función del controlador de hacer clic en la página. En la salida, el componente produce un paginador formado. Para mostrar los botones, se usa otro componente de
botón secundario. Si hay muchas páginas, el paginador no las muestra todas, las combina y las muestra en forma de puntos suspensivos.

Código de componente del paginadorimport React from 'react'; import classes from './Paginator.css'; import Button from '../../UI/Button/Button'; const Paginator = (props) => { const { tp, cp, pageClickHandler } = props; let paginator = null; if (tp !== undefined && tp > 0) { let buttons = []; buttons.push( <Button key={`pback`} disabled={cp === 1} clicked={(cp === 1 ? null : event => pageClickHandler(event, 'back'))}> ← </Button> ); const isDots = (i, tp, cp) => i > 1 && i < tp && (i > cp + 1 || i < cp - 1) && (cp > 4 || i > 5) && (cp < tp - 3 || i < tp - 4); let flag; for (let i = 1; i <= tp; i++) { const dots = isDots(i, tp, cp) && (isDots(i - 1, tp, cp) || isDots(i + 1, tp, cp)); if (flag && dots) { flag = false; buttons.push( <Button key={`p${i}`} className={classes.Dots} disabled={true}> ... </Button> ); } else if (!dots) { flag = true; buttons.push( <Button key={`p${i}`} disabled={i === cp} clicked={(i === cp ? null : event => pageClickHandler(event, i))}> {i} </Button> ); } } buttons.push( <Button key={`pforward`} disabled={cp === tp} clicked={(cp === tp ? null : event => pageClickHandler(event, 'forward'))}> → </Button> ); paginator = <div className={classes.Paginator}> {buttons} </div> } return paginator; } export default Paginator;
Código de componente de botón import React from 'react'; import classes from './Button.css'; const button = (props) => ( <button disabled={props.disabled} className={classes.Button + (props.className ? ' ' + props.className : '')} onClick={props.clicked}> {props.children} </button> ); export default button;
Broma
Jest es una biblioteca de código abierto bien conocida para la prueba unitaria del código JavaScript. Fue creado y desarrollado gracias a Facebook. Escrito en Node.js.
En términos generales, el significado de las pruebas se reduce al hecho de que necesita encontrar parámetros de entrada para su código e inmediatamente describir la salida que debe producir su código. Al realizar pruebas, Jest ejecuta su código con parámetros de entrada y compara el resultado con el esperado. Si coincide, la prueba pasará, y si no, no pasará.
Un pequeño ejemplo de
jestjs.io .
Supongamos que tenemos un módulo Node.js, que es una función que agrega dos números (archivo
sum.js ):
function sum(a, b) { return a + b; } module.exports = sum;
Si nuestro módulo se guarda en un archivo, para probarlo, necesitamos crear el archivo
sum.test.js en el que escribir dicho código para probar:
const sum = require('./sum'); test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); });
En este ejemplo, usando la función de
prueba , creamos una prueba llamada
'agrega 1 + 2 para igualar 3' . El segundo parámetro para la función de
prueba , pasamos una función que realmente realiza la prueba.
La prueba consiste en el hecho de que ejecutamos nuestra función de
suma con los parámetros de entrada
1 y
2 , pasamos el resultado a la función Jest
expect () . Luego, utilizando la función Jest
toBe () , el resultado transmitido se compara con el esperado (
3 ). La función
toBe () pertenece a la categoría de funciones de prueba Jest (matchers).
Para la prueba, simplemente vaya a la carpeta del proyecto y llame a
broma en la línea de comando. Jest encontrará el archivo con la extensión
.test.js y ejecutará la prueba. Aquí está el resultado que dará a conocer:
PASS ./sum.test.js ✓ adds 1 + 2 to equal 3 (5ms)
Pruebas de componentes y enzimas.
La prueba de instantáneas es una característica relativamente nueva en Jest. El punto es que con la ayuda de una función de prueba especial, le pedimos a Jest que guarde una instantánea de nuestra estructura de datos en el disco, y durante las ejecuciones de prueba posteriores, compare nuevas instantáneas con las guardadas previamente.
La instantánea en este caso no es más que una representación textual de los datos. Por ejemplo, una instantánea de algún objeto se verá así (la clave de matriz aquí es el nombre de la prueba):
exports[`some test name`] = ` Object { "Hello": "world" } `;
Así es como se ve la función de prueba de Jest, que realiza la comparación de imágenes (parámetros opcionales):
expect(value).toMatchSnapshot(propertyMatchers, snapshotName)
El
valor puede ser cualquier estructura de datos serializable. Por primera vez, la función
toMatchSnapshot () simplemente escribe la instantánea en el disco; en tiempos posteriores, ya realizará la comparación.
Muy a menudo, esta tecnología de prueba se usa específicamente para probar componentes React, y aún más precisamente, para probar la representación correcta de los componentes React. Para hacer esto, debe pasar el componente como
valor después de la representación.
Enzyme es una biblioteca que simplifica enormemente las pruebas de las aplicaciones React al proporcionar prácticas funciones de representación de componentes. Enzyme se desarrolla en Airbnb.
Enzyme le permite renderizar componentes en código. Hay varias funciones convenientes para esto que realizan diferentes opciones de representación:
- representación completa (como en el navegador, representación completa del DOM);
- renderizado simplificado (renderizado superficial);
- representación estática
No profundizaremos en las opciones de representación, para la prueba de instantáneas, la representación estática es suficiente, lo que le permite obtener un código HTML estático del componente y sus componentes secundarios:
const wrapper = render(<Foo title="unique" />);
Entonces, representamos nuestro componente y pasamos el resultado a
expect () , y luego llamamos a la función
.toMatchSnapshot () . La función
it es solo una abreviatura de la función de
prueba .
... const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); ...
Cada vez que se
ejecuta la prueba
, toMatchSnapshot () compara dos instantáneas:
esperada (que se escribió previamente en el disco) y
actual (que se obtuvo durante la prueba actual).
Si las imágenes son idénticas, la prueba se considera aprobada. Si hay una diferencia en las imágenes, la prueba se considera no aprobada y se muestra al usuario la diferencia entre las dos imágenes en forma de diferencias (como en los sistemas de control de versiones).
Aquí hay un ejemplo de salida de Jest cuando la prueba falla. Aquí vemos que tenemos un botón adicional en la imagen actual.

En esta situación, el usuario debe decidir qué hacer. Si se planean cambios en la instantánea debido a cambios en el código del componente, entonces debe sobrescribir la instantánea anterior con una nueva. Y si los cambios son inesperados, entonces debe buscar un problema en su código.
Daré un ejemplo completo para probar un paginador (archivo
Paginator.test.js ).
Para una prueba de paginador más conveniente, creé una función de
instantánea (tp, cp) que tomará dos parámetros: el número total de páginas y la página actual. Esta función realizará una prueba con los parámetros dados. Todo lo que queda es llamar a la función
snapshoot () con varios parámetros (incluso en un bucle) y probar, probar ...
import React from 'react'; import { configure, render } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import Paginator from './Paginator'; configure({ adapter: new Adapter() }); describe('Paginator', () => { const snapshoot = (tp, cp) => { const wrapper = render(<Paginator tp={tp} cp={cp} />); it(`Total = ${tp}, Current = ${cp}`, () => { expect(wrapper).toMatchSnapshot(); }); } snapshoot(0, 0); snapshoot(1, -1); snapshoot(1, 1); snapshoot(2, 2); snapshoot(3, 1); for (let cp = 1; cp <= 10; cp++) { snapshoot(10, cp); } });
¿Por qué necesitaba un generador de informes adicional?
Cuando comencé a trabajar con esta tecnología de prueba, la sensación de un enfoque inicial inacabado no me dejó. Después de todo, las imágenes solo se pueden ver como texto.
Pero, ¿qué pasa si algún componente produce mucho código HTML al renderizar? Aquí hay un componente de paginador de 3 botones. Una instantánea de dicho componente se verá así:
exports[`Paginator Total = 1, Current = -1 1`] = ` <div class="Paginator" > <button class="Button" > ← </button> <button class="Button" > 1 </button> <button class="Button" > → </button> </div> `;
Primero debe asegurarse de que la versión original del componente se represente correctamente. No es muy conveniente hacer esto simplemente viendo el código HTML en forma de texto. Pero estos son solo tres botones. ¿Y si necesita probar, por ejemplo, una mesa o algo aún más voluminoso? Además, para realizar una prueba completa, debe ver muchas imágenes. Será bastante incómodo y difícil.
Luego, si la prueba falla, debe comprender cómo difiere la apariencia de los componentes. La diferencia de su código HTML, por supuesto, le permitirá comprender lo que ha cambiado, pero nuevamente, la oportunidad de ver personalmente la diferencia no será superflua.
En general, pensé que sería necesario para que las imágenes pudieran verse en el navegador de la misma forma que se ven en la aplicación. Incluyendo con estilos aplicados a ellos. Así que tuve la idea de mejorar el proceso de prueba de instantáneas escribiendo un generador de informes adicional para Jest.
Mirando hacia adelante, eso es lo que tengo. Cada vez que ejecuto las pruebas, mi constructor actualiza el libro de instantáneas. Directamente en el navegador, puede ver los componentes tal como se ven en la aplicación, así como también mirar inmediatamente el código fuente de las imágenes y diff (si la prueba falla).

Jest Report Builders
Los creadores de Jest han brindado la oportunidad de escribir creadores de informes adicionales. Esto se hace de la siguiente manera. Debe escribir un módulo en Node.JS que debe tener uno o más de estos métodos:
onRunStart ,
onTestStart ,
onTestResult ,
onRunComplete , que corresponden a varios eventos de progreso de prueba.
Entonces necesitas conectar tu módulo en la configuración de Jest. Hay una directiva especial para
periodistas para esto. Si desea incluir su constructor además, debe agregarlo al final de la matriz de
reporteros .
Después de eso, Jest llamará a los métodos de su módulo en ciertas etapas de la ejecución de la prueba, pasando los resultados actuales a los métodos. El código en estos métodos, de hecho, debería crear informes adicionales que necesita. Entonces, en términos generales, se ve la creación de creadores de informes adicionales.
Cómo funciona Jest-snapshots-book
No inserto específicamente el código del módulo en el artículo, ya que lo mejoraré aún más. Se puede encontrar en mi GitHub, este es el
archivo src / index.js en la página del proyecto.
Se llama a mi creador de informes al finalizar las pruebas. Puse el código en el
método onRunComplete (contextos, resultados) . Funciona de la siguiente manera.
En la propiedad
results.testResults , Jest pasa una matriz de resultados de prueba a esta función. Cada resultado de la prueba incluye una ruta al archivo de prueba y una matriz de mensajes con los resultados. Mi generador de informes busca cada archivo de prueba con un archivo de instantánea correspondiente. Si se detecta un archivo de instantánea, el generador de informes crea una página HTML en el libro de instantáneas y lo escribe en la carpeta del libro de instantáneas en la carpeta raíz del proyecto.
Para generar una página HTML, el generador de informes, utilizando la función recursiva
grabCSS (moduleName, css = [], level = 0), recopila todos los estilos, comenzando desde el componente bajo prueba y más abajo en el árbol de todos los componentes que importa. Por lo tanto, la función recopila todos los estilos necesarios para que el componente se muestre correctamente. Los estilos recopilados se agregan a la página HTML del libro de instantáneas.
Utilizo módulos CSS en mis proyectos, así que no estoy seguro de si esto funcionará si no se utilizan módulos CSS.
Si la prueba pasa, el constructor inserta un iFrame en la página HTML con la imagen en dos opciones de visualización: el código fuente (la imagen tal como es) y el componente después de la representación. La opción de visualización en iFrame se cambia haciendo clic con el mouse.
Si no se pasó la prueba, entonces todo es más complicado. Jest proporciona en este caso solo el mensaje que muestra en la consola (vea la captura de pantalla anterior).
Contiene diferencias e información adicional sobre la prueba fallida. De hecho, en este caso, estamos tratando esencialmente con dos imágenes: la
esperada y la
real . Si tenemos el esperado: se almacena en el disco en la carpeta de instantáneas, entonces la instantánea Jest actual no proporciona.
Por lo tanto, tuve que escribir el código que aplica la diferencia Jest tomada del mensaje a la instantánea esperada y crea una instantánea real basada en la esperada. Después de eso, el constructor muestra junto al iFrame la instantánea de iFrame esperada de la instantánea actual, que puede cambiar su contenido entre tres opciones: el código fuente, el componente después de la representación, diff.
Así es como se ve el resultado del generador de informes si configura la opción verbose = true para él.

Enlaces utiles
PS
La prueba de instantáneas no es suficiente para probar completamente una aplicación React. Solo cubre la representación de sus componentes. También es necesario probar su funcionamiento (reacciones a las acciones del usuario, por ejemplo). Sin embargo, la prueba de instantáneas es una forma muy conveniente de asegurarse de que sus componentes se procesen según lo previsto. Y jest-snapshots-book hace que el proceso sea un poco más fácil.