Pruebas de extremo a extremo con Joomla! y Cypress - Mis primeros pasos y pensamientos

Las pruebas automatizadas no son una herramienta especial para los desarrolladores de software en proyectos grandes. Especialmente para las extensiones, las pruebas automatizadas son una ayuda para identificar rápidamente los problemas. Ayudan a garantizar que las extensiones funcionen sin problemas en las versiones más nuevas de Joomla.

Los desarrolladores de Joomla Core quieren que los desarrolladores de software de terceros prueben sus extensiones, para encontrar errores antes de que un usuario los encuentre. Esto requiere mucho tiempo y es un trabajo aburrido. Por lo tanto, a menudo no se hace. Especialmente si tiene que hacerse. manualmente por humanos para cada nueva versión. Las pruebas automatizadas hacen posible repetir los pasos manuales para cada versión sin que una persona realice los pasos ellos mismos. De esta manera, los errores se encuentran antes de que un usuario los encuentre al acceder al sistema en vivo.

Por cierto, cualquiera que quiera revisar Cypress encontrará este texto un buen lugar para comenzar. Puede probar problemas sin tener que instalar y configurar todo usted mismo. En el repositorio Github del proyecto Joomla todo está listo para funcionar.

Introducción

"Si bien es cierto que la calidad no se puede probar, es igualmente evidente que sin pruebas es imposible desarrollar algo de calidad". - [James A. Whittaker]

Antes de encontrarme con Cypress por primera vez, no podía haber imaginado que las barreras que a menudo se interponían en el camino de mis pruebas en realidad se apartarían un poco. Pasé mucho tiempo probando software, y antes incluso más tiempo lidiando con los problemas. ¡que surgió por falta de pruebas! Ahora estoy convencido de que las pruebas que son:

  • lo más cerca posible en el tiempo de la programación,
  • automáticamente,
  • con frecuencia, idealmente después de cada cambio de programa,

    traiga más de lo que cuesta. Y lo que es más: las pruebas pueden ser incluso divertidas.

¡Vale la pena aprender métodos de prueba! Los métodos de prueba son duraderos, porque se pueden usar no solo con cualquier lenguaje de programación, sino que se pueden aplicar a casi cualquier trabajo humano. Debe probar casi todo lo importante en la vida de vez en cuando. Los métodos de prueba son independientes de las herramientas de software específicas.A diferencia de las técnicas de programación o los lenguajes de programación, que a menudo están de moda y pasados ​​de moda, el conocimiento de cómo configurar buenas pruebas es atemporal.

¿Quién debería leer este texto?

Cualquiera que piense que las pruebas de software son una pérdida de tiempo deberían echar un vistazo a este artículo. En particular, me gustaría invitar a leerlo a los desarrolladores que siempre han querido escribir pruebas para su software, pero nunca lo han hecho para una variedad El ciprés podría ser una forma de eliminar tales barreras.

algo de teoria

el triangulo magico

El Triángulo Mágico describe la relación entre los costos, el tiempo requerido y la calidad alcanzable. Originalmente, esta relación se reconoció y describió en la gestión de proyectos. Sin embargo, probablemente haya oído hablar de esta tensión en otras áreas también. Problema en casi todas las operaciones procesos en una empresa.

Por ejemplo, generalmente se asume que un costo más alto tiene un impacto positivo en la calidad y/o la fecha de finalización, es decir, el tiempo.

 

El Triángulo Mágico en la Gestión de Proyectos: si se invierte más dinero en el proyecto, esto tiene un impacto positivo en la calidad o el tiempo.

Al revés, un ahorro de costos obligará a que la calidad disminuya y/o se retrase la finalización.

El Triángulo Mágico en la Gestión de Proyectos: si se invierte menos dinero en el proyecto, esto tiene un impacto negativo en la calidad o el tiempo.

Ahora entra en juego la magia: ¡Superamos la correlación entre tiempo, costos y calidad!, porque a la larga esto se puede superar.

La conexión entre tiempo, costos y calidad puede superarse a largo plazo.

Tal vez usted también haya experimentado en la práctica que una reducción de la calidad no se traduce en un ahorro de costes a largo plazo, sino que la deuda técnica que esto crea a menudo conduce incluso a aumentos de costes y tiempo adicional.

A la larga, la correlación entre costo, tiempo y calidad puede superarse.

La deuda técnica se refiere al esfuerzo adicional que implica realizar cambios y mejoras en un software mal programado en comparación con un software bien escrito. Martin Fowler distingue los siguientes tipos de deuda técnica: las que se han contraído deliberadamente y las que se han contraído inadvertidamente. También distingue entre deudas técnicas prudentes e imprudentes.

Deuda técnica

Costos y beneficios

En la literatura encontrará estadísticas devastadoras sobre las posibilidades de éxito de los proyectos de software.Poco ha cambiado en la imagen negativa que ya se registró en un estudio de AW Feyhl en la década de 1990. Aquí, en un análisis de 162 proyectos en 50 organizaciones s , Se determinó la desviación de costos en comparación con la planificación original: ¡70% de los proyectos mostraron una desviación de costos de al menos 50%! ¡Algo no está bien! No puede simplemente aceptar eso, ¿verdad?

Una solución sería prescindir por completo de las estimaciones de costos y seguir la argumentación del movimiento #NoEstimates . Este movimiento opina que las estimaciones de costos en un proyecto de software no tienen sentido. Según la opinión de #NoEstimates, un proyecto de software siempre contiene la producción de algo nuevo Lo nuevo no es comparable con las experiencias ya existentes y, por lo tanto, no es predecible.

Cuanta más experiencia obtengo, más llego a la conclusión de que las vistas extremas no son buenas. La solución casi siempre está en el medio. Evite los extremos también en los proyectos de software y busque un punto medio. No tiene que tener un 100% plan seguro. Pero tampoco debe comenzar un nuevo proyecto ingenuamente. Aunque la gestión de proyectos de software y especialmente la estimación de costos es un tema importante, no lo aburriré más en este texto. El enfoque de este artículo es mostrar cómo E2E las pruebas se pueden integrar en el flujo de trabajo práctico del desarrollo de software.

Integre las pruebas de software en su flujo de trabajo

Ha decidido probar su software. ¡Genial! ¿Cuál es el mejor momento para hacerlo? Echemos un vistazo al costo de corregir un error en las diferentes fases del proyecto. Cuanto antes encuentre un error, menor será el costo de corregirlo. .

Costos relativos para la solución de problemas en varias etapas del proyecto

Prueba y depuración: hay palabras que a menudo se mencionan al mismo tiempo y cuyos significados, por lo tanto, se igualan. En una inspección más cercana, sin embargo, los términos representan diferentes interpretaciones. Prueba y depuración pertenecen a estas palabras. Los dos términos tienen en común que detectan disfunciones, pero también hay diferencias en el significado.

  • Las pruebas encuentran fallas desconocidas durante el desarrollo. Encontrar la falla es costoso, mientras que la localización y eliminación del error es barata.
  • Los depuradores corrigen los fallos de funcionamiento que se encuentran después de que el producto está terminado. Encontrar el fallo de funcionamiento es gratuito, pero localizar y corregir el error es costoso.

Conclusión: tiene más sentido comenzar a integrar las pruebas lo antes posible. Desafortunadamente, esto es difícil de implementar en un proyecto de código abierto como Joomla con la mayoría de los contribuyentes voluntarios.

Integración Continua (CI)
Integración continua de pruebas

Imagine el siguiente escenario. Está a punto de lanzarse una nueva versión de un popular sistema de administración de contenido. Todo lo que los desarrolladores del equipo han contribuido desde el último lanzamiento ahora se usa en conjunto por primera vez. ¡La tensión está aumentando! ¿Funciona? ¿Todas las pruebas serán exitosas, si el proyecto integra pruebas en absoluto. ¿O el lanzamiento de la nueva versión tendrá que posponerse nuevamente y se avecinan horas angustiosas de corrección de errores? Por cierto, posponer la fecha de lanzamiento tampoco es bueno para la imagen del producto de software! A ningún desarrollador le gusta experimentar este escenario. Es mucho mejor saber en cualquier momento en qué estado se encuentra actualmente el proyecto de software? El código que no encaja con el existente solo debe integrarse después han sido "hechos a la medida".Especialmente en tiempos en los que es cada vez más común que se deba corregir una brecha de seguridad, ¡un proyecto siempre debe poder crear una versión!Y aquí es donde entra en juego la integración continua.

En la integración continua, los elementos individuales del software se integran de forma permanente. El software se crea y prueba en ciclos pequeños. De esta manera, encontrará problemas durante la integración o pruebas defectuosas en una etapa temprana y no días o semanas después. Con una integración exitosa, La solución de problemas es mucho más fácil porque los errores se descubren cerca del momento de la programación y, por lo general, solo una pequeña parte del programa se ve afectada. Joomla integra el nuevo código mediante la integración continua. El nuevo código se integra solo cuando se pasan todas las pruebas.

Con una integración continua de software nuevo, la solución de problemas es mucho más fácil porque los errores se descubren cerca del momento de la programación y, por lo general, solo una pequeña parte del programa se ve afectada.

Para asegurarse de tener pruebas para todas las partes del programa disponibles en todo momento durante la integración continua, debe desarrollar un software basado en pruebas.

Desarrollo dirigido por pruebas (TDD)

El desarrollo basado en pruebas es una técnica de programación que utiliza el desarrollo en pequeños pasos. Primero se escribe el código de prueba. Solo entonces se crea el código del programa que se va a probar. Cualquier cambio en el programa se realiza solo después de que se haya creado el código de prueba para ese cambio. sido creado. Por lo tanto, sus pruebas fallan inmediatamente después de la creación. La función requerida aún no está implementada en el programa. Solo entonces crea el código del programa real, es decir, el código del programa que satisface la prueba.

Las pruebas TDD te ayudan a escribir el programa correctamente .

Cuando escuche por primera vez sobre esta técnica, es posible que no se sienta cómodo con el concepto. ""Humano"" siempre quiere hacer algo productivo primero, después de todo. Y escribir pruebas no parece productivo a primera vista. Pruébelo. A veces ¡te haces amigo de una nueva técnica solo después de conocerla! En proyectos con una alta cobertura de pruebas, me siento más cómodo cuando agrego nuevas funciones.

Si revisa la parte del ejercicio al final del texto, puede probarlo. Primero cree la prueba y luego escriba el código para Joomla Core. Luego envíe todo junto como PR en Github. Si todos hicieran esto , Joomla tendría una cobertura de prueba ideal.

Desarrollo impulsado por el comportamiento (BDD)

BDD no es otra técnica de programación o técnica de prueba, sino una especie de mejor práctica para el desarrollo de software. BDD se usa idealmente junto con TDD. En principio, Behaviour-Driven-Development significa probar no la implementación del código del programa, sino la ejecución. - es decir, el comportamiento del programa Una prueba comprueba si se cumple la especificación, es decir, el requisito del cliente.

Cuando desarrolla software de una manera impulsada por el comportamiento, las pruebas no solo lo ayudan a escribir el programa correctamente, sino que también lo ayudan a escribir el programa correcto .

¿Qué quiero decir con eso: "Escribir el programa correcto"? Sucede que los usuarios ven las cosas de manera diferente a los desarrolladores. El flujo de trabajo de eliminar un artículo en Joomla es un ejemplo. Una y otra vez me encuentro con usuarios que hacen clic en el ícono de estado en el y se sorprenden. El usuario por lo general supone intuitivamente que el elemento ahora se eliminó de forma permanente, pero se cambia de la papelera a activarse. Para el desarrollador, hacer clic en el ícono de estado es un cambio de estado, una alternancia en todas las demás vistas. ¿Por qué debería ser diferente en la papelera? Para el desarrollador, la función se implementa sin errores. Joomla funciona correctamente. Pero en mi opinión, la función no es la correcta en ese lugar, porque la mayoría de los usuarios la describirían/solicitarían de manera bastante diferente. .

En el desarrollo impulsado por el comportamiento, los requisitos para el software se describen a través de ejemplos llamados escenarios o historias de usuarios.

  • una fuerte participación del usuario final en el proceso de desarrollo del software,
  • la documentación de todas las fases del proyecto con historias de usuarios/ejemplos de casos en forma de texto, generalmente en el lenguaje de descripción en el lenguaje de descripción Gherkin,
  • pruebas automáticas de estas historias de usuario/estudios de casos,
  • implementación sucesiva. Por lo tanto, se puede acceder en cualquier momento a una descripción del software que se implementará. Con la ayuda de esta descripción, puede garantizar continuamente la corrección del código del programa ya implementado.

El proyecto Joomla introdujo BDD en un proyecto Google Summer of Code . Se esperaba que los usuarios sin conocimientos de programación pudieran participar más fácilmente usando Gherkin ). El enfoque no tuvo un seguimiento constante. En ese momento, Joomla usó Codeception como un herramienta de prueba Con Cypress, el desarrollo BDD también es posible desarrollar en la forma BDD.

Planificación

Tipos de prueba
  • Pruebas unitarias: una prueba unitaria es una prueba que prueba las unidades de programa más pequeñas de forma independiente.
  • Pruebas de integración: Una prueba de integración es una prueba que prueba la interacción de unidades individuales.
  • Pruebas E2E o Pruebas de Aceptación: Una prueba de aceptación verifica si el programa cumple con la tarea definida al principio.
Estrategias

Si desea agregar una nueva función en Joomla y asegurarla con pruebas, puede proceder de dos maneras.

Top-down y bottom-up son dos enfoques fundamentalmente diferentes para comprender y presentar problemas complejos. Top-down va paso a paso de lo abstracto y general a lo concreto y específico. Para ilustrar esto con un ejemplo: un sistema de gestión de contenido como Joomla generalmente presenta sitios web en un navegador.Concretamente, sin embargo, hay una serie de pequeñas subtareas en este proceso.Una de ellas es la tarea de mostrar un texto específico en un encabezado.

Bottom-up describe la dirección opuesta: en este punto, vale la pena recordar una vez más que un elemento del desarrollo dirigido por el comportamiento es la creación de una descripción textual del comportamiento del software. Esta descripción de los criterios de aceptación ayuda a crear pruebas, especialmente la parte superior. Pruebas de nivel extremo a extremo o pruebas de aceptación.

El enfoque habitual para crear pruebas hoy en día es desde abajo. Si prefiere el desarrollo de software basado en el comportamiento, debe usar la estrategia opuesta. Debe usar la estrategia de arriba hacia abajo. Con una estrategia de arriba hacia abajo, un malentendido se identifica desde el principio. en la fase de diseño.

Estrategias de prueba: prueba de arriba hacia abajo y prueba de abajo hacia arriba

  • Pruebas de arriba hacia abajo: cuando se aplica la estrategia de arriba hacia abajo, uno comienza con las pruebas de aceptación, es decir, con la parte del sistema que está más estrechamente relacionada con los requisitos del usuario. Para el software escrito para usuarios humanos, esta suele ser la interfaz de usuario. La atención se centra en probar cómo interactúa un usuario con el sistema. Una desventaja de las pruebas de arriba hacia abajo es que se debe dedicar mucho tiempo a crear duplicados de prueba. Los componentes que aún no están integrados deben reemplazarse por marcadores de posición. Hay Al principio no hay un código de programa real. Por lo tanto, las partes que faltan deben crearse artificialmente. Poco a poco, estos datos artificiales se reemplazan por datos realmente calculados.

  • Pruebas ascendentes: si sigue la estrategia ascendente, comienza con pruebas unitarias. Al principio, el desarrollador tiene el estado objetivo en mente. Sin embargo, luego primero divide este objetivo en componentes individuales. El problema con el El enfoque de abajo hacia arriba es que es difícil probar cómo se usará un componente más tarde en situaciones reales. La ventaja de las pruebas de abajo hacia arriba es que hemos terminado las partes del software muy rápidamente. Sin embargo, estas partes deben usarse con precaución. Funcionan correctamente, eso es lo que aseguran las pruebas unitarias, pero no se asegura si el resultado final es realmente lo que el cliente imagina que es el software.

La pirámide de prueba de Mike Cohn

¿Cuántas pruebas se deben implementar de qué tipo de prueba? La pirámide de prueba de Mike Cohn describe un concepto para el empleo de las pruebas de software automatizadas. La pirámide consta de tres niveles, estructurados según la frecuencia de uso y la relevancia. ‍

Idealmente, la base de la pirámide de prueba está formada por muchas pruebas unitarias rápidas y fáciles de mantener, de esta manera, la mayoría de los errores se pueden detectar rápidamente.

En el nivel medio están las pruebas de integración, brindan servicios para la prueba dirigida de interfaces críticas, los tiempos de ejecución de las pruebas de integración son más largos y su mantenimiento también es más complejo que el de las pruebas unitarias.

La parte superior de la pirámide consiste en pruebas E2E lentas, que a veces requieren mucho mantenimiento. Las pruebas E2E son muy útiles para probar la aplicación como un sistema completo.

Requisitos

¿Qué equipo necesitas para trabajar en la siguiente parte práctica?

¿Qué requisitos tienes para trabajar la siguiente parte práctica de forma activa? No tienes que cumplir muchos requisitos para poder trabajar los contenidos de este manual. Por supuesto, debes tener un ordenador. Un entorno de desarrollo con Git, NodeJS y Composer y un servidor web local deben estar instalados o instalables en él.

¿Qué conocimientos debe tener usted personalmente?

Debes conocer técnicas básicas de programación, lo ideal es que ya hayas programado una pequeña aplicación web, en cualquier caso, debes saber dónde almacenar archivos en tu computadora de desarrollo y cómo cargarlos en tu navegador de Internet.

Pruébelo. Integre las pruebas en su próximo proyecto. Tal vez su primera experiencia con una prueba le ahorrará una tediosa sesión de depuración o un error vergonzoso en el sistema real. Después de todo, con una red segura de pruebas, puede desarrollar software con menos estrés.

configurando

¡Configurando Cypress con Joomla!

En la versión para desarrolladores disponible en Github, Joomla está configurado para Cypress. Ya hay pruebas que puede usar como guía. Por lo tanto, no es necesario configurar todo usted mismo para obtener una primera descripción general. De esta manera, puede experimentar con Cypress. , conozca sus ventajas y desventajas y decida usted mismo si desea utilizar la herramienta de prueba.

Pasos para configurar el entorno local:

Clone el repositorio en la raíz de su servidor web local:

$ git clone https://github.com/joomla/joomla-cms.git

Navegue a la carpeta joomla-cms:

$ cd joomla-cms

Según Joomla Roadmap, la próxima versión principal 5.0 se lanzará en octubre de 2023. Para estar actualizado, uso esta versión de desarrollo aquí.

Cambiar a la rama 5.0-dev :

$ git checkout 5.0-dev

Instale todos los paquetes de compositores necesarios:

$ composer install

Instale todos los paquetes npm necesarios:

$ npm install

Para obtener más información y ayuda sobre cómo configurar su estación de trabajo, consulte el artículo de documentación de Joomla "Configuración de su estación de trabajo para el desarrollo de Joomla" . Para Cypress, hay información en cypress.io . Pero eso no es necesario en este momento. Joomla configura todo para usted Solo necesita configurar sus datos individuales a través del archivo de configuración joomla-cms/cypress.config.js.

Configura tus datos individuales, para esto puedes usar la plantilla joomla-cms/cypress.config.dist.jscomo orientación, en mi caso este archivo se ve así:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  fixturesFolder: 'tests/cypress/fixtures',
  videosFolder: 'tests/cypress/output/videos',
  screenshotsFolder: 'tests/cypress/output/screenshots',
  viewportHeight: 1000,
  viewportWidth: 1200,
  e2e: {
    setupNodeEvents(on, config) {},
    baseUrl: 'http://localhost/joomla-cms',
    specPattern: [
      'tests/cypress/integration/install/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/administrator/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/site/**/*.cy.{js,jsx,ts,tsx}'
    ],
    supportFile: 'tests/cypress/support/index.js',
    scrollBehavior: 'center',
    browser: 'firefox',
    screenshotOnRunFailure: true,
    video: false
  },
  env: {
    sitename: 'Joomla CMS Test',
    name: 'admin',
    email: Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Concretamente agregué el directorio tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}al specPattern Array, porque quiero guardar allí la prueba de los módulos más adelante. Luego cambié el usuario y las contraseñas porque también quiero probar la instalación manualmente y recordar mejor las autoasignadas. Uso un contenedor Docker. como base de datos. Por lo tanto, cambié el servidor de la base de datos y los datos de acceso. Y finalmente tuve que configurar la URL raíz http://localhost/joomla-cmsde mi instalación de Joomla.

Usar ciprés

A través del navegador web

Llame npm run cypress:opena través de CLI en su directorio raíz de Joomla. Poco tiempo después, se abrirá la aplicación Cypress. Hemos creado previamente el archivo joomla-cms/cypress.config.dist.js. Que esto se detecte se puede ver en el hecho de que E2E Testing se especifica como configurado.

La aplicación Cypress se abre después de llamar a 96;npm run cypress:open96;.

Aquí puede elegir si desea ejecutar las pruebas E2E y qué navegador desea utilizar.Para el ejemplo, elegí la opción "Iniciar prueba en Firefox".

Pruebas E2E en la aplicación Cypress: seleccione el navegador a utilizar.

Se enumerarán todos los conjuntos de pruebas disponibles y puede hacer clic en el que desea ejecutar. Cuando seleccione un conjunto de pruebas, las pruebas se ejecutarán y podrá ver la ejecución de las pruebas en tiempo real en el navegador.

Conjunto de pruebas de Joomla en Firefox a través de la aplicación Cypress.

Mientras se ejecutan las pruebas, puede ver el script ejecutado en un lado y el resultado en el navegador en el lado derecho. Estas no son solo capturas de pantalla, sino instantáneas reales del navegador en ese momento, para que pueda ver el código HTML real. También son posibles capturas de pantalla e incluso vídeos de las pruebas.

Prueba de instalación de Joomla en curso.

Pruébalo, si lo usas db_host: 'localhost',puedes probar la instalación y así haber configurado Joomla correctamente para el trabajo en la siguiente parte de este texto.

Si usted, como yo, usa una fuente externa (no lcoalhost; uso un contenedor docker) como db_host, la prueba para este tipo de instalación aún no está lista. En ese caso, hay una pregunta de seguridad en la rutina de instalación, que es aún no considerado en las pruebas. En este caso, instale Joomla manualmente con la información ingresada en el archivo joomla-cms/cypress.config.js. Las siguientes pruebas usarán la configuración de este archivo de configuración, por ejemplo, para iniciar sesión en el área de administración de Joomla. De esta manera, el desarrollador de la prueba hace No tiene que preocuparse por ingresar los datos de inicio de sesión. El usuario y la contraseña coincidentes siempre se usan automáticamente desde el archivo de configuración.

Sin cabeza

De forma predeterminada, cypress runejecuta todas las pruebas sin encabezado . El siguiente comando ejecuta todas las pruebas ya codificadas y guarda capturas de pantalla en el directorio /joomla-cms/tests/cypress/output/screenshotsen caso de error. El directorio de salida se estableció en el cypress.config.jsarchivo.

$ npm run cypress:run

Otros comandos CLI

Hay otros comandos útiles que no están implementados como secuencias de comandos en el package.jsonproyecto de Joomla. Los ejecuto a través de npx [docs.npmjs.com/commands/npx].

ciprés verificar

El cypress verifycomando verifica que Cypress esté instalado correctamente y se pueda ejecutar.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
información de ciprés

El cypress infocomando genera información sobre Cypress y el entorno actual.

$ npx cypress info
Displaying Cypress info...

Detected 2 browsers installed:

1. Chromium
  - Name: chromium
  - Channel: stable
  - Version: 113.0.5672.126
  - Executable: chromium
  - Profile: /.../snap/chromium/current

2. Firefox
  - Name: firefox
  - Channel: stable
  - Version: 113.0.1
  - Executable: firefox
  - Profile: /.../snap/firefox/current/Cypress/firefox-stable

Note: to run these browsers, pass : to the '--browser' field

Examples:
- cypress run --browser chromium
- cypress run --browser firefox

Learn More: https://on.cypress.io/launching-browsers

Proxy Settings: none detected
Environment Variables: none detected

Application Data: /.../.config/cypress/cy/development
Browser Profiles: /.../.config/cypress/cy/development/browsers
Binary Caches: /.../.cache/Cypress

Cypress Version: 12.8.1 (stable)
System Platform: linux (Ubuntu - 22.04)
System Memory: 4.08 GB free 788 MB
versión ciprés

El cypress versioncomando imprime la versión binaria Cypress instalada, la versión del paquete Cypress, la versión de Electron utilizada para crear Cypress y la versión del nodo incluido.

$ npx cypress version
Cypress package version: 12.8.1
Cypress binary version: 12.8.1
Electron version: 21.0.0
Bundled Node version: 16.16.0

La documentación de Cypress proporciona información más detallada.

Escribiendo la primera prueba propia

Si todo ha funcionado hasta ahora, podemos comenzar a crear nuestras propias pruebas.

Obtenga una descripción general

Aprendiendo de pruebas ya desarrolladas

En la versión de desarrollo del CMS de Joomla ya hay pruebas de Cypress. Estas están en la carpeta /tests/System/integration. Aquellos que les gusta aprender con el ejemplo encontrarán una introducción adecuada aquí.

Importar código para tareas repetitivas

Los desarrolladores de Joomla están trabajando en el proyecto Joomla-cypress de NodeJs , que proporciona código de prueba para casos de prueba comunes. Estos se importan durante la instalación de la versión de desarrollador del CMS usando npm installa través de

  • package.jsony a través de la
  • archivo de soporte /tests/System/support/index.jsEl archivo de soporte se define en la configuración cypress.config.js.
// package.json
{
  "name": "joomla",
  "version": "5.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0-or-later",
  "repository": {
    "type": "git",
    "url": "https://github.com/joomla/joomla-cms.git"
  },
...
  "devDependencies": {
    ...
    "joomla-cypress": "^0.0.16",
    ...
  }
}

Un ejemplo es hacer clic en un botón de la barra de herramientas. Por ejemplo, Cypress.Commands.add('clickToolbarButton', clickToolbarButton)hace que el comando clickToolbarButton()esté disponible en las pruebas personalizadas y al cy.clickToolbarButton('new')hacer clic en el botón Newse simula. El código requerido para esto se muestra en el codenipped a continuación.

// node_modules/joomla-cypress/src/common.js
...
const clickToolbarButton = (button, subselector = null) => {
  cy.log('**Click on a toolbar button**')
  cy.log('Button: ' + button)
  cy.log('Subselector: ' + subselector)

  switch (button.toLowerCase())
  {
    case "new":
      cy.get("#toolbar-new").click()
      break
    case "publish":
      cy.get("#status-group-children-publish").click()
      break
    case "unpublish":
      cy.get("#status-group-children-unpublish").click()
      break
    case "archive":
      cy.get("#status-group-children-archive").click();
      break
    case "check-in":
      cy.get("#status-group-children-checkin").click()
      break
    case "batch":
      cy.get("#status-group-children-batch").click()
      break
    case "rebuild":
      cy.get('#toolbar-refresh button').click()
      break
    case "trash":
      cy.get("#status-group-children-trash").click()
      break
    case "save":
      cy.get("#toolbar-apply").click()
      break
    case "save & close":
      cy.get(".button-save").contains('Save & Close').click()
      break
    case "save & new":
      cy.get("#save-group-children-save-new").click()
      break
    case "cancel":
      cy.get("#toolbar-cancel").click()
      break
    case "options":
      cy.get("#toolbar-options").click()
      break
    case "empty trash":
    case "delete":
      cy.get("#toolbar-delete").click()
      break
    case "feature":
      cy.get("#status-group-children-featured").click()
      break
    case "unfeature":
      cy.get("#status-group-children-unfeatured").click()
      break
    case "action":
      cy.get("#toolbar-status-group").click()
      break
    case "transition":
      cy.get(".button-transition.transition-" + subselector).click()
      break
  }

  cy.log('--Click on a toolbar button--')
}

Cypress.Commands.add('clickToolbarButton', clickToolbarButton)
...

El siguiente código muestra otro ejemplo, el ingreso al área de administración.

// /node_modules/joomla-cypress/src/user.js
...
const doAdministratorLogin = (user, password, useSnapshot = true) => {
  cy.log('**Do administrator login**')
  cy.log('User: ' + user)
  cy.log('Password: ' + password)

  cy.visit('administrator/index.php')
  cy.get('#mod-login-username').type(user)
  cy.get('#mod-login-password').type(password)
  cy.get('#btn-login-submit').click()
  cy.get('h1.page-title').should('contain', 'Home Dashboard')

  cy.log('--Do administrator login--')
}

Cypress.Commands.add('doAdministratorLogin', doAdministratorLogin)

...
Tareas comunes en el entorno individual.

En el directorio /tests/System/supportencontrará tareas comunes en el entorno individual.Para que puedan reutilizarse fácilmente, se importan a través del archivo de soporte.Un /tests/System/support/index.jsejemplo de una tarea que se repite con frecuencia es el inicio de sesión en el área de administración, que se maneja en el archivo. usando la /tests/System/support/commands.jsfuncióndoAdministratorLogin

El siguiente código también muestra cómo cypress.config.jsse usa la información de la configuración en las pruebas.Se Cypress.env('username')le asigna el valor de la usernamepropiedad dentro del grupo env.

Además, aquí podemos ver cómo sobrescribir comandos, Cypress.Commands.overwrite('doAdministratorLogin' ...),sobrescribe el código que acabamos de ver en el paquete joomla-cypress, la ventaja es que usuario y contraseña se utilizan automáticamente desde la configuración individual.

// /tests/System/support/commands.js
...
Cypress.Commands.overwrite('doAdministratorLogin', (originalFn, username, password, useSnapshot = true) => {
  // Ensure there are valid credentials
  const user = username ?? Cypress.env('username');
  const pw = password ?? Cypress.env('password');

  // Do normal login when no snapshot should be used
  if (!useSnapshot) {
    // Clear the session data
    Cypress.session.clearAllSavedSessions();

    // Call the normal function
    return originalFn(user, pw);
  }

  // Do login through the session
  return cy.session([user, pw, 'back'], () => originalFn(user, pw), { cacheAcrossSpecs: true });
});
...

Instalar su propia extensión de Joomla

Para ver cómo probar su propio código, instalaremos un componente de ejemplo simple a través del backend de Joomla.El archivo para la instalación se puede descargar desde Codeberg .

Instale su propia extensión de Joomla.

Después de la instalación, puede encontrar un enlace a la vista del componente Foo en la barra lateral izquierda del backend de Joomla.

Vista del componente de ejemplo en el backend de Joomla.

Ahora tenemos un entorno de prueba configurado y un código para probar.

La primera prueba propia

Manos

Al probar el backend, notará que debe iniciar cada prueba con un inicio de sesión. Podemos evitar este código redundante usando la beforeEach()función. Este llamado enlace ejecuta el código que ingresamos antes de ejecutar cada prueba. De ahí el nombre beforeEach().

Cypress proporciona varios tipos de enlaces , incluidos beforeenlaces afterque se ejecutan antes o después de las pruebas en un grupo de pruebas, y beforeEachenlaces afterEachque se ejecutan antes o después de cada prueba individual en el grupo. Los enlaces se pueden definir globalmente o dentro de un describedbloque específico. El siguiente El ejemplo de código en el archivo tests/System/integration/administrator/components/com_foos/FoosList.cy.jshace que se realice un inicio de sesión en el backend antes de cada prueba dentro del describedbloque test com_foos features.

Ahora comenzamos con la parte práctica y creamos el archivo tests/System/integration/administrator/components/com_foos/FoosList.cy.jscon los siguientes códigos copiados antes de escribir la primera prueba productiva. ¡Nuestro primer ejemplo debería iniciarnos con éxito en el backend antes de cualquier prueba! Probaremos esto después de crear la primera prueba.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

})

Nota: Los ganchos, que se implementan dentro del archivo /tests/System/support/index.js, se aplican a cada archivo de prueba en el conjunto de prueba.

Una prueba exitosa

El componente que instalamos para la prueba contiene los tres elementos Astridy NinaPrimero Elmar, probamos si estos elementos se crearon correctamente.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')

    cy.checkForPhpNoticesOrWarnings()
  })
})

Nota: La función checkForPhpNoticesOrWarnings()que encuentras en el archivo /node_modules/joomla-cypress/src/support.js.

Obtenemos el elemento DOM maina través del comando Cypress get

Debería encontrar su prueba recién creada FooList.cy.jsen la lista de pruebas disponibles en la barra lateral izquierda. Si este no es el caso, cierre el navegador y ejecútelo npm run cypress:opende nuevo.

Prueba de ejecución de Joomla para su propia extensión.

Haga clic en el nombre de la prueba para ejecutarla. Debería terminar con éxito y verá mensajes verdes.

La vista después de que la prueba se haya ejecutado correctamente.

Una prueba fallida

Agregue la línea cy.get('main').should('contain.text', 'Sami')al archivo de prueba para que la ejecución falle. No hay ningún elemento con este nombre. Después de guardar el archivo de prueba, Cypress nota el cambio. Después de cada cambio, Cypress vuelve a ejecutar automáticamente todas las pruebas en el archivo de prueba.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')
    cy.get('main').should('contain.text', 'Sami')

    cy.checkForPhpNoticesOrWarnings()
  })
})

Como era de esperar, la prueba falla. Hay mensajes rojos. Puede ver el código de cada paso de la prueba en la barra lateral izquierda. Por lo tanto, es posible encontrar el motivo del error. Para cada paso hay una instantánea del documento HTML, para que pueda verificar el marcado en cualquier momento. Esto es útil, especialmente durante el desarrollo.

La vista después de la prueba ha fallado.

Ejecutar solo una prueba en un archivo

Nuestra extensión de demostración contiene más de un diseño. Agregue una prueba para probar el diseño de estado vacío. Dado que ahora tenemos dos pruebas en este archivo, Cypress siempre ejecutará ambas pruebas cada vez que guardemos el archivo. Podemos usar para que solo una .only()prueba es ejecutado:

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
    beforeEach(() => {
        cy.doAdministratorLogin()
    })

    it('list view shows items', function () {
        cy.visit('administrator/index.php?option=com_foos')

        cy.get('main').should('contain.text', 'Astrid')
        cy.get('main').should('contain.text', 'Nina')
        cy.get('main').should('contain.text', 'Elmar')

        cy.checkForPhpNoticesOrWarnings()
    })

    it.only('emptystate layout', function () {
        cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
        cy.get('main').should('contain.text', 'No Foo have been created yet.')
    })
})

Durante el desarrollo, esto es muy conveniente.

Atributos de prueba especiales

Ahora nos gusta probar el frontend para nuestro componente.Hacemos esto en un archivo separado /tests/System/integration/site/components/com_foos/FooItem.cy.js.

La mayoría de las veces usamos una clase CSS para obtener elementos en las pruebas de Joomla. Si bien esto es perfectamente válido y funcionará, en realidad no se recomienda. ¿Por qué no? Cuando usa clases CSS o ID, vincula sus pruebas a cosas que lo más probable es que cambie con el tiempo. Las clases y los ID son para el diseño, el diseño y, a veces, a través de JavaScript para el control, que puede cambiar fácilmente. Si alguien cambia el nombre de una clase o ID, sus pruebas ya no funcionarán. Para que sus pruebas sean menos frágiles y más preparado para el futuro, Cypress recomienda crear atributos de datos especiales para sus elementos específicamente con fines de prueba.

Usaré el data-testatributo para los elementos.Primero agrego el atributo data-test="foo-main"al código de producción.

// /components/com_foos/tmpl/foo/default.php

\defined('_JEXEC') or die;
?>
<div data-test="foo-main">
Hello Foos
</div>

Luego pruebo el código de producción buscando el atributo [data-test="foo-main"].

// tests/System/integration/site/components/com_foos/FooItem.cy.js
describe('Test com_foo frontend', () => {
  it('Show frondend via query in url', function () {
    cy.visit('index.php?option=com_foos&view=foo')

    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')

    cy.checkForPhpNoticesOrWarnings()
  })
})
Probar un elemento del menú y algunas ideas sobre eventos, esperas y mejores prácticas

Ahora me gusta probar la creación de un elemento de menú para nuestro componente. Hago esto en un archivo separado /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js. Este código es complejo y muestra muchas características especiales.

Primero, definí una constante en la que configuro todas las propiedades relevantes del elemento del menú. Esto tiene la ventaja de que, en caso de cambios de una propiedad relevante, tengo que ajustar solo en un lugar:

const testMenuItem = {
  'title': 'Test MenuItem',
  'menuitemtype_title': 'COM_FOOS',
  'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
}

A continuación, verá el código completo del archivo MenuItem.cy.js:

// tests/System/integration/administrator/components/com_foos/MenuItem.cy.js

describe('Test menu item', () => {
  beforeEach(() => {
    cy.doAdministratorLogin(Cypress.env('username'), Cypress.env('password'))
  })

  it('creates a new menu item', function () {
    const testMenuItem = {
      'title': 'Test MenuItem',
      'menuitemtype_title': 'COM_FOOS',
      'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
    }

    cy.visit('administrator/index.php?option=com_menus&view=item&client_id=0&menutype=mainmenu&layout=edit')
    cy.checkForPhpNoticesOrWarnings()
    cy.get('h1.page-title').should('contain', 'Menus: New Item')

    cy.get('#jform_title').clear().type(testMenuItem.title)

    cy.contains('Select').click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_title).click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_entry).click()

    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_list')
    cy.clickToolbarButton('Save & Close')
    cy.wait('@item_list')
    cy.get('#system-message-container').contains('Menu item saved.').should('exist')

    // Frontend
    cy.visit('index.php')
    cy.get('.sidebar-right').contains(testMenuItem.title).click()
    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')
    cy.checkForPhpNoticesOrWarnings()

    // Trash
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.clickToolbarButton('Action')
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_trash')
    cy.clickToolbarButton('trash')
    cy.wait('@item_trash')
    cy.get('#system-message-container').contains('Menu item trashed.').should('exist')

    // Delete
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.setFilter('published', 'Trashed')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.on("window:confirm", (s) => {
      return true;
    });
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_delete')
    cy.clickToolbarButton('empty trash');
    cy.wait('@item_delete')
    cy.get('#system-message-container').contains('Menu item deleted.').should('exist')
  })
})
  • En este código, puede ver un ejemplo de probar algo y luego eliminarlo todo, restaurando así el estado inicial. De esta manera, puede repetir las pruebas tantas veces como desee. Sin restaurar el estado inicial, la segunda prueba fallará porque Joomla no puede almacenar dos elementos similares.

Nota: Una prueba debe ser:

  • repetible
  • mantenido simple En términos concretos, esto significa que debe probar un problema limitado y el código para esto no debe ser demasiado extenso.
  • independiente de otras pruebas.
  • Y puede ver cómo usar una ruta interceptada definida con cy.intercept()[^docs.cypress.io/api/commands/intercept] como alias, y luego esperar la ruta definida como alias con cy.wait().

Al escribir pruebas para este tipo de aplicaciones, uno se siente tentado a utilizar valores aleatorios como cy.wait(2000);en el cy.waitcomando. El problema con este enfoque es que, si bien esto puede funcionar bien en el desarrollo. Sin embargo, no se garantiza que funcione siempre. ¿Por qué? Porque el sistema subyacente depende de cosas que son difíciles de predecir, por lo tanto, siempre es mejor definir exactamente lo que está esperando.

  • El código también muestra cómo esperar una alerta y confirmarla .
cy.on("window:confirm", (s) => {
  return true;
});
  • Por último, pero no menos importante, el código de prueba contiene Cypress incorporado y funciones típicas de Joomla que pueden ser reutilizadas por los desarrolladores de extensiones. Por ejemplo, cy.setFilter('published', 'Trashed')o cy.clickToolbarButton('Save & Close')son funciones en las que se pueden encontrar soluciones para pruebas individuales en general y que los desarrolladores de Joomla en particular a menudo necesitan. .
Mezclar código asíncrono y sincronizado

Los comandos de Cypress son asincrónicos, es decir, no devuelven un valor, sino generateeste. Cuando iniciamos Cypress, no ejecuta los comandos inmediatamente, sino que los lee en serie y los pone en cola. Si mezcla código asincrónico y sincrónico en las pruebas, puede obtener resultados inesperados. Si ejecuta el siguiente código, obtendrá un error en contra de las expectativas. Seguramente también habría esperado que mainText = $main.text()cambiara el valor mainText. Pero mainText === 'Initial'aún es válido al final. ¿Por qué? Cypress primero ejecuta el código síncrono en al principio y al final. Solo entonces llama a la parte asíncrona dentro then(). Eso significa que la variable mainTextse inicializa e inmediatamente después se verifica, si ha cambiado, lo que por supuesto no es el caso.

let mainText = 'Initial';
cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
cy.get("main").then(
  ($main) => (mainText = $main.text())
);

if (mainText === 'Initial') {
  throw new Error(`Der Text hat sich nicht geändert. Er lautet: ${mainText}`);
}

El procesamiento de la cola se vuelve bastante claro y visual si se observa la ejecución del siguiente código en la consola del navegador, el texto 'Cypress Test.' aparece mucho antes de que se muestre el contenido del elemento, aunque las líneas de código mainson en un orden diferente.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Talones y espías

A stubes una forma de simular el comportamiento de la función de la que dependen las pruebas. En lugar de llamar a la función real, el código auxiliar reemplaza esa función y devuelve un objeto predefinido. Por lo general, se usa en pruebas unitarias, pero también se puede usar para fines -pruebas hasta el final.

A spyes similar a stub, pero no exactamente igual. No cambia el comportamiento de una función, sino que la deja como está. Captura cierta información sobre cómo se llama a la función. Por ejemplo, para comprobar si se llama a la función. con los parámetros correctos, o para contar con qué frecuencia se llama a la función.

El siguiente ejemplo muestra a spyy a stuben acción. Via const stub = cy.stub()creamos el stubelemento y determinamos en el siguiente paso que falsese devuelve para la primera llamada y truepara la segunda. Usando cy.on('window:confirm', stub)hacemos que se stubuse para window:confirm'. En el siguiente paso creamos con cy.spy(win, 'confirm').as('winConfirmSpy')el Spyelemento , que observa la llamada de 'window:confirm'. Ahora probamos que en la primera llamada se rechaza la eliminación de la categoría y en la segunda llamada se confirma. Al hacerlo, asegura stubque podemos esperar con certeza cuáles serán los valores de retorno entregado. 'window:confirm'está encapsulado. @winConfirmSpyayuda a garantizar que la función se llamó realmente, y con qué frecuencia se llamó.

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
...
const stub = cy.stub()

stub.onFirstCall().returns(false)
stub.onSecondCall().returns(true)

cy.on('window:confirm', stub)

cy.window().then(win => {
  cy.spy(win, 'confirm').as('winConfirmSpy')
})

cy.intercept('index.php?option=com_categories&view=categories&extension=com_foos').as('cat_delete')
cy.clickToolbarButton('empty trash');

cy.get('@winConfirmSpy').should('be.calledOnce')
cy.get('main').should('contain.text', testFoo.category)


cy.clickToolbarButton('empty trash');
cy.wait('@cat_delete')

cy.get('@winConfirmSpy').should('be.calledTwice')

cy.get('#system-message-container').contains('Category deleted.').should('exist')
...

Si solo se trata de establecer un valor fijo para la 'window:confirm'llamada, el siguiente código hará el trabajo.

cy.on("window:confirm", (s) => {
  return true;
});

conclusión

En este artículo, ha visto la teoría básica y las características prácticas de las pruebas E2E con Cypress. Usé la instalación de Joomla para demostrar cómo escribir diferentes pruebas para garantizar que un componente de Joomla en un sitio web funcione como se espera. También mostré cómo personalizar Cypress. Test Runner en el archivo cypress.json y cómo usar los comandos personalizados de Cypress. Esto se hizo usando ejemplos fáciles de seguir.

Espero que hayas disfrutado el recorrido por Cypress usando Joomla como ejemplo y que hayas podido llevarte mucho conocimiento e inspiración para ti.

An online collaborative community manual for Joomla! users, developers or anyone interested in learning more about Joomla! Currently, we have more than 9,000 articles written, maintained, and translated by our Joomla! community members. 

Tenga en cuenta que este sitio web utiliza un sistema de traducción automática para ayudar en la traducción de los diferentes idiomas. Pedimos disculpas por cualquier error o error tipográfico que se pueda mostrar en los diferentes textos.