Una guía rápida para escribir pruebas de extremo a extremo con jest-puppeteer
Este artículo proporciona una guía rápida para escribir pruebas de extremo a extremo eficientes con jest-puppeteer, enfatizando el proceso de configuración, las API comúnmente utilizadas y los escenarios de prueba prácticos utilizando una aplicación simple de tareas pendientes como ejemplo.
Como parte de nuestro compromiso para garantizar la calidad de Logto y la mejora continua, empleamos jest-puppeteer para realizar pruebas automatizadas de extremo a extremo. Esto nos permite iterar rápidamente en el desarrollo de Logto sin interrupciones.
En este artículo, compartiremos nuestra experiencia escribiendo scripts de prueba eficientes de jest-puppeteer utilizando una simple aplicación de tareas pendientes como ejemplo. Nuestro objetivo es ayudarte a comenzar rápidamente a escribir código de pruebas jest-puppeteer para tus propios proyectos.
Introducción a las pruebas de extremo a extremo con jest-puppeteer
Las pruebas de extremo a extremo son una forma de asegurar que tu aplicación funcione correctamente desde la perspectiva del usuario. Para lograr esto, utilizamos dos herramientas esenciales: [Jest] (https://jestjs.io/) y [Puppeteer] (https://pptr.dev/).
[Jest] (https://jestjs.io/) es un popular marco de pruebas de JavaScript que ofrece una API fácil de usar para escribir pruebas y afirmaciones. Se utiliza ampliamente para pruebas unitarias e integradas.
[Puppeteer] (https://pptr.dev/) es una biblioteca Node.js desarrollada por el equipo de Chrome que proporciona una API de alto nivel para controlar los navegadores Chrome o Chromium sin cabeza. Esto lo convierte en una opción ideal para automatizar interacciones del navegador en pruebas de extremo a extremo.
[jest-puppeteer] (https://github.com/argos-ci/jest-puppeteer) es un preestablecido de Jest que permite pruebas de extremo a extremo con Puppeteer. Ofrece una API sencilla para lanzar nuevas instancias de navegador e interactuar con las páginas web a través de ellas.
Ahora que tienes una comprensión básica de las herramientas esenciales, vamos a sumergirnos en la configuración de tu entorno de pruebas.
Preparación de tu entorno de pruebas
Configurar tu entorno de pruebas para pruebas de extremo a extremo con jest-puppeteer es un proceso sencillo que implica tres pasos principales:
- Instalar dependencias
En tu proyecto (o crea un nuevo proyecto), abre la terminal y ejecuta:
Luego ve a node_modules/puppeteer
e instala Chromium (que es necesario para Puppeteer):
- Configurar Jest
Después, necesitas configurar Jest para que funcione sin problemas con Puppeteer.
Crea un archivo de configuración de Jest (por ejemplo, jest.config.js
) en el directorio raíz de tu proyecto si todavía no tienes uno. En este archivo de configuración, especifica jest-puppeteer como un preestablecido:
Puedes personalizar otras configuraciones de Jest en este archivo según sea necesario. Para obtener más información sobre cómo personalizar las configuraciones de Jest, consulta la [Configuración de Jest] (https://jestjs.io/docs/configuration).
- Escribe tus pruebas
Crea archivos de prueba en tu proyecto, generalmente denominados con la extensión .test.js
. Jest descubrirá y ejecutará automáticamente estos archivos de prueba.
Aquí tienes un ejemplo del [documento de Jest] (https://jestjs.io/docs/puppeteer):
Luego ejecuta el comando para ejecutar la prueba:
Siguiendo estos tres pasos, tendrás un entorno de pruebas bien configurado para realizar pruebas de extremo a extremo utilizando jest-puppeteer.
Sin embargo, ten en cuenta que este es solo un ejemplo básico. Para obtener información más detallada sobre la configuración del entorno, consulta la documentación relevante:
- [Jest: Comenzando] (https://jestjs.io/docs/getting-started)
- [Jest: Usando con puppeteer] (https://jestjs.io/docs/puppeteer)
- [Puppeteer] (https://pptr.dev/)
API comúnmente utilizadas
En los próximos pasos, confiaremos en las API proporcionadas por Jest, Puppeteer y jest-puppeteer para ayudarnos en nuestras pruebas.
Jest ofrece principalmente API para organizar pruebas y afirmar resultados esperados. Puedes explorar los detalles específicos en la [documentación] (https://jestjs.io/docs/api).
Las API de Puppeteer están diseñadas principalmente para interactuar con los navegadores, y su soporte para pruebas puede ser menos sencillo. Puedes consultar la [documentación de Puppeteer] (https://pptr.dev/) para entender las funcionalidades que ofrece. En nuestros ejemplos de prueba posteriores, cubriremos algunos casos de uso comunes.
Dado que las API de Puppeteer no fueron diseñadas originalmente para pruebas, su uso para escribir pruebas puede resultar desafiante. Para simplificar el proceso de escribir pruebas de Puppeteer, jest-puppeteer incluye una biblioteca incorporada llamada expect-puppeteer
. Ofrece un conjunto de API concisas y amigables para el usuario. La biblioteca se detalla en su documentación, que presenta diversas características útiles, como se muestra en los ejemplos a continuación:
En los siguientes ejemplos, combinaremos las API proporcionadas por estas bibliotecas para completar nuestros escenarios de prueba.
Ahora es el momento de comenzar a aprender a escribir código de prueba utilizando nuestra simple aplicación de tareas pendientes.
La simple aplicación de tareas pendientes
Suponiendo que tenemos una simple aplicación de tareas pendientes ejecutándose en http://localhost:3000
, el código HTML principal para esta aplicación es el siguiente:
Cuando realizamos pruebas de extremo a extremo, nuestro objetivo es cubrir los siguientes escenarios:
- Cuando accedemos a la URL de la aplicación, la aplicación y sus datos deberían cargarse correctamente.
- Se debería poder hacer clic en el botón de verificación del artículo.
- Al hacer clic en un enlace externo dentro de las notas del artículo, debería abrirse el enlace en una nueva pestaña.
- Puede agregar un artículo desde el formulario.
Más adelante, escribiremos código de prueba para validar estos escenarios.
Espera que la aplicación y sus datos se hayan cargado
Consideramos que la aplicación se ha cargado con éxito cuando se cumplen las siguientes condiciones:
- Un elemento con el ID "app" debería existir en la página después de acceder a la URL de la aplicación.
- Los datos dentro de la aplicación deberían representarse correctamente.
Entonces, hemos escrito el siguiente código de prueba:
En el código:
page.goto
es equivalente a ingresar "http://localhost:3000" en el navegador, lo que te permite navegar a cualquier URL.page.waitForSelector
se utiliza para esperar a que un selector CSS específico coincida con un elemento en particular, con un tiempo de espera predeterminado de 30 segundos.expect(page).toMatchElement
proviene de la bibliotecaexpect-puppeteer
, es similar apage.waitForSelector
, pero también soporta emparejar el texto dentro de un elemento. Además, el tiempo de espera predeterminado paratoMatchElement
es solo de 500 ms.
Puede parecer perfecto a primera vista, pero cuando ejecutamos esta prueba y la desplegamos en un entorno CI, ocasionalmente falla después de múltiples ejecuciones. El mensaje de error indica:
Basándonos en la información de falla y en el hecho de que esta prueba pasa la mayoría de las veces, podemos deducir que los datos solicitados por la aplicación pueden no siempre volver dentro de los primeros 500 ms después de la carga de la aplicación. Por lo tanto, queremos esperar a que los datos se carguen antes de hacer la afirmación.
Normalmente, hay dos enfoques comunes para lograr esto:
- Aumenta el tiempo de espera de la afirmación
Desde el mensaje de error, podemos ver que el tiempo de espera predeterminado para toMatchElement
está configurado a 500 ms. Podemos aumentar el tiempo de espera agregando la opción timeout
a la función así:
Este enfoque puede reducir la aparición de pruebas fallidas en cierta medida, pero no resuelve completamente el problema porque no podemos saber con certeza cuánto tiempo se necesita para recuperar los datos.
Por lo tanto, solo usamos este enfoque cuando estamos seguros sobre el tiempo de espera necesario, como en escenarios como "un tooltip aparece después de pasar el cursor sobre un elemento durante más de 2 segundos."
- Espera a que se complete la solicitud de red antes de hacer una afirmación
Este es el enfoque correcto. Aunque tal vez no sepamos cuánto tiempo llevará la solicitud de datos, esperar a que se complete la solicitud de red siempre es una opción segura. En este punto, podemos usar page.waitForNavigation({ waitUntil: 'networkidle0' })
para esperar a que se completan las solicitudes de red:
De esta manera, antes de que ejecutemos la afirmación para la aplicación y sus datos cargados, podemos asegurarnos de que nuestras solicitudes de red ya hayan concluido. Esto asegura que la prueba genere consistentemente los resultados correctos.
Espera hacer clic en un botón específico
A continuación, estamos probando la funcionalidad de hacer clic en el botón de verificación dentro de un artículo.
En la aplicación de ejemplo, hemos notado que todos los artículos comparten la misma estructura. Sus botones de verificación tienen el selector CSS li[class$=item] > button
, y el texto del botón siempre es "Check". Esto significa que no podemos especificar directamente en qué botón de verificación de qu é artículo hacer clic. Por lo tanto, necesitamos una nueva solución.
Supongamos que queremos hacer clic en el botón de verificación del artículo "Leer documento de inicio de Logto". Podemos desglosar esto en dos pasos:
- Primero, obtener una referencia de ese artículo específico.
- Luego, hacer clic en el botón de verificación ubicado dentro de ese artículo.
Como se muestra en el código, usamos la función toMatchElement
para emparejar el artículo con el itemName
configurado en "Leer documento de inicio de Logto". Luego, usamos la referencia readDocItem
para hacer clic en el botón con el contenido de texto 'Check' ubicado debajo de ella.
Es importante notar que al obtener el readDocItem
, el selector CSS utilizado es li[class$=item]:has(div[class$=itemName])
. Este selector empareja el elemento li
raíz del artículo, no el itemName
dentro de él, porque tenemos la intención de hacer clic en el button
bajo la etiqueta li
más tarde.
El uso de expect(readDocItem).toClick
es similar a toMatchElement
. En el código de ejemplo, pasamos { text: 'Check' }
para emparejar aún más el textContent
del botón. Sin embargo, puedes decidir si quieres o no emparejar el contenido de texto del botón según tus necesidades.
Espera que se abra un enlace externo en una nueva pestaña
A continuación, queremos probar si podemos abrir un enlace externo en una nueva pestaña al hacer clic en él dentro de las notas del artículo.
En la aplicación de ejemplo, hemos encontrado que el artículo "Leer documento de inicio de Logto" tiene un enlace externo al documento de Logto dentro de sus notas. Aquí está nuestro código de prueba:
En el código, utilizamos toClick
para hacer clic en el enlace dentro de itemNotes
.
Posteriormente, usamos browser.waitForTarget
para capturar una pestaña recién abierta con la URL "https://docs.logto.io/docs/tutorials/get-started."
Después de obtener la pestaña, usamos target.page()
para obtener una instancia de la página de documentos de Logto y verificar más si la página se ha cargado.
Además, recuerda cerrar la página recién abierta después de completar nuestra prueba.
Espera crear un artículo a partir del formulario
Ahora, queremos probar la funcionalidad de agregar un artículo.
Necesitamos rellenar el nombre del artículo y las notas del artículo en el formulario, hacer clic en el botón "Agregar" y verificar si el contenido que agregamos aparece en la lista:
Observarás que usamos la función expect(page).toFill(inputSelector, content)
para rellenar el contenido del formulario. Esta función reemplaza todo el contenido del input con el content
.
Si quieres añadir algunos caracteres al input sin reemplazar el contenido existente, puedes usar page.type(selector, content)
para agregar directamente el contenido deseado al input.
Sin embargo, cuando tenemos muchos campos para rellenar en nuestro formulario, llamar a toFill
varias veces no es conveniente. En este caso, podemos usar el siguiente enfoque para rellenar todo el contenido en una sola llamada:
La clave del objeto proporcionado es el atributo de nombre del elemento de entrada.
Resumen
Hemos cubierto un ejemplo simple para introducir requisitos de prueba comunes y técnicas correspondientes para escribir código al utilizar jest-puppeteer para pruebas de extremo a extremo. Esperamos que esto te ayude a captar rápidamente los conceptos básicos de la escritura de código de prueba jest-puppeteer.