Test end-to-end con Joomla! e Cypress - I miei primi passi e pensieri

I test automatizzati non sono uno strumento speciale per gli sviluppatori di software in progetti di grandi dimensioni.In particolare per le estensioni, i test automatizzati sono un aiuto per identificare rapidamente i problemi.Aiutano a garantire che le estensioni funzionino senza problemi nelle versioni più recenti di Joomla.

Gli sviluppatori di Joomla Core vogliono che gli sviluppatori di software di terze parti testino le loro estensioni, per trovare i bug prima che un utente li trovi. Questo richiede molto tempo ed è un lavoro noioso. Pertanto spesso non viene fatto. Soprattutto non se deve essere fatto manualmente da esseri umani per ogni nuova versione. I test automatizzati consentono di ripetere i passaggi manuali per ogni versione senza che un essere umano esegua i passaggi stessi. In questo modo, i bug vengono rilevati prima che un utente li incontri quando accede al sistema live.

A proposito, chiunque voglia dare un'occhiata a Cypress troverà questo testo un buon punto di partenza. Puoi testare i problemi senza dover installare e configurare tutto da solo. Nel repository Github del progetto Joomla tutto è pronto per partire.

Introduzione

"Mentre è vero che la qualità non può essere testata, è altrettanto evidente che senza test è impossibile sviluppare qualcosa di qualità." – [James A. Whittaker]

Prima di incontrare Cypress per la prima volta, non avrei potuto immaginare che le barriere che spesso ostacolavano i miei test sarebbero state in qualche modo spostate.Ho passato molto tempo a testare il software e prima ancora di più ad affrontare i problemi che è sorto a causa della mancanza di test!Ora sono convinto che i test che sono:

  • il più vicino possibile nel tempo alla programmazione,
  • automaticamente,
  • frequentemente - idealmente dopo ogni cambio di programma,

    portare più di quanto costano E per di più: i test possono anche essere divertenti.

Vale la pena imparare i metodi di test!I metodi di test sono duraturi, perché possono essere utilizzati non solo con qualsiasi linguaggio di programmazione, ma possono essere applicati a quasi tutti i lavori umani.Dovresti testare quasi tutto ciò che è importante nella vita una volta ogni tanto. I metodi di test sono indipendenti da specifici strumenti software.A differenza delle tecniche di programmazione o dei linguaggi di programmazione, che sono spesso di moda e fuori moda, la conoscenza di come impostare buoni test è senza tempo.

Chi dovrebbe leggere questo testo?

Chiunque pensi che testare il software sia una perdita di tempo dovrebbe dare un'occhiata a questo articolo.In particolare, vorrei invitare a leggerlo quegli sviluppatori che hanno sempre voluto scrivere test per il loro software, ma non l'hanno mai fatto per una varietà di ragioni Cypress potrebbe essere un modo per rimuovere tali barriere.

Qualche teoria

Il triangolo magico

Il Triangolo Magico descrive la relazione tra i costi, il tempo richiesto e la qualità ottenibile.In origine, questa relazione era riconosciuta e descritta nella gestione del progetto.Tuttavia, probabilmente avrai sentito parlare di questa tensione anche in altre aree.problema in quasi tutte le attività operative processi in un'azienda.

Ad esempio, si presume generalmente che un costo più elevato abbia un impatto positivo sulla qualità e/o sulla data di completamento, ovvero sul tempo.

 

Il triangolo magico nella gestione dei progetti - Se vengono investiti più soldi nel progetto, ciò ha un impatto positivo sulla qualità o sul tempo.

Al contrario, un risparmio sui costi costringerà la qualità a diminuire e/o il completamento a essere ritardato.

Il triangolo magico nella gestione dei progetti - Se si investe meno denaro nel progetto, ciò ha un impatto negativo sulla qualità o sul tempo.

Ora entra in gioco la magia: superiamo la correlazione tra tempo, costi e qualità!Perché, a lungo andare, questo può effettivamente essere superato.

La connessione tra tempo, costi e qualità può essere superata nel lungo periodo.

Forse anche voi avete sperimentato nella pratica che una riduzione della qualità non si traduce in un risparmio di costi a lungo termine: il debito tecnico che ne deriva spesso porta anche ad aumenti dei costi e tempi aggiuntivi.

Nel lungo periodo, la correlazione tra costo, tempo e qualità può essere effettivamente superata.

Il debito tecnico si riferisce allo sforzo aggiuntivo richiesto per apportare modifiche e miglioramenti a un software programmato non buono rispetto a un software ben scritto.Martin Fowler distingue i seguenti tipi di debito tecnico: quelli in cui si è entrati deliberatamente e quelli in cui si è entrati inavvertitamente Distingue anche tra debiti tecnici prudenti e sconsiderati.

Debito tecnico

Costi e benefici

In letteratura si trovano statistiche devastanti sulle possibilità di successo dei progetti software, poco è cambiato nel quadro negativo che già si registrava in uno studio di AW Feyhl negli anni 90. Qui, in un'analisi di 162 progetti in 50 organizzazioni , è stato determinato lo scostamento dei costi rispetto alla pianificazione originale: il 70% dei progetti ha mostrato uno scostamento dei costi di almeno il 50%! Qualcosa non va! Non puoi semplicemente accettarlo, vero?

Una soluzione sarebbe quella di rinunciare del tutto alle stime dei costi e seguire l'argomentazione del movimento #NoEstimates . Questo movimento è dell'opinione che le stime dei costi in un progetto software non hanno senso. Un progetto software contiene secondo l'opinione di #NoEstimates sempre la produzione di qualcosa di nuovo.Il nuovo non è confrontabile con esperienze già esistenti e quindi non prevedibile.

Più esperienza faccio, più arrivo alla conclusione che le visioni estreme non vanno bene. La soluzione è quasi sempre nel mezzo. Evita gli estremi anche nei progetti software e cerca una via di mezzo. Non devi avere un 100% certo piano. Ma non dovresti nemmeno iniziare un nuovo progetto ingenuamente. Sebbene la gestione dei progetti software e in particolare la stima dei costi sia un argomento importante, non ti annoierò più con questo in questo testo. L'obiettivo di questo articolo è mostrare come E2E i test possono essere integrati nel flusso di lavoro pratico dello sviluppo del software.

Integra i test del software nel tuo flusso di lavoro

Hai deciso di testare il tuo software. Ottimo! Qual è il momento migliore per farlo? Diamo un'occhiata al costo della correzione di un bug nelle diverse fasi del progetto. Prima trovi un bug, minore è il costo per risolverlo .

Costi relativi per la risoluzione dei problemi nelle varie fasi del progetto

Testing e Debugging: ci sono parole che vengono spesso menzionate nello stesso respiro e i cui significati sono quindi equiparati. A un esame più attento, tuttavia, i termini si prestano a interpretazioni diverse. Testing e debugging appartengono a queste parole. I due termini hanno in comune che rilevano malfunzionamenti, ma ci sono anche differenze nel significato.

  • I test rilevano malfunzionamenti sconosciuti durante lo sviluppo: trovare il malfunzionamento è costoso, mentre la localizzazione e l'eliminazione dell'errore sono economiche.
  • I debugger correggono i malfunzionamenti che vengono rilevati dopo che il prodotto è finito. Trovare il malfunzionamento è gratuito, ma individuare e correggere l'errore è costoso.

Conclusione: ha più senso iniziare a integrare i test il prima possibile, ma sfortunatamente è difficile da implementare in un progetto open source come Joomla con contributori per lo più volontari.

Integrazione continua (CI)
Integrazione continua dei test

Immagina il seguente scenario. Una nuova versione di un popolare sistema di gestione dei contenuti sta per essere rilasciata. Tutto ciò che gli sviluppatori del team hanno contribuito dall'ultima versione viene ora utilizzato insieme per la prima volta. La tensione sta aumentando! funziona? Tutti i test avranno successo, ammesso che il progetto integri i test, o il rilascio della nuova versione dovrà essere nuovamente posticipato e ci attendono ore snervanti di correzione dei bug? buono per l'immagine del prodotto software! A nessuno sviluppatore piace sperimentare questo scenario. È molto meglio sapere in qualsiasi momento in quale stato si trova attualmente il progetto software? Il codice che non si adatta a quello esistente dovrebbe essere integrato solo dopo sono stati "fatti su misura".Soprattutto in tempi in cui è sempre più comune dover correggere un gap di sicurezza, un progetto dovrebbe sempre essere in grado di creare un rilascio!Ed è qui che entra in gioco l'integrazione continua.

Nell'integrazione continua, i singoli elementi del software sono integrati in modo permanente. Il software viene creato e testato in piccoli cicli. In questo modo, si riscontrano problemi durante l'integrazione o test errati in una fase iniziale e non giorni o settimane dopo. Con un'integrazione riuscita, La risoluzione dei problemi è molto più semplice perché gli errori vengono scoperti vicino al momento della programmazione e di solito solo una piccola parte del programma è interessata.Joomla integra il nuovo codice utilizzando l'integrazione continua.Il nuovo codice viene integrato solo quando tutti i test vengono superati.

Con una continua integrazione di nuovo software, la risoluzione dei problemi è molto più semplice perché gli errori vengono scoperti vicino al momento della programmazione e di solito solo una piccola parte del programma è interessata.

Per assicurarti di avere test per tutte le parti del programma disponibili in ogni momento durante l'integrazione continua, dovresti sviluppare un software basato sui test.

Sviluppo basato su test (TDD)

Lo sviluppo basato su test è una tecnica di programmazione che utilizza lo sviluppo in piccoli passaggi. Prima si scrive il codice di test. Solo successivamente si crea il codice del programma da testare. Qualsiasi modifica al programma viene apportata solo dopo che il codice di test per quella modifica è stato stato creato. Quindi i tuoi test falliscono immediatamente dopo la creazione. La funzione richiesta non è ancora implementata nel programma. Solo allora crei il codice del programma vero e proprio, cioè il codice del programma che soddisfa il test.

I test TDD aiutano a scrivere correttamente il programma .

Quando senti parlare per la prima volta di questa tecnica, potresti non sentirti a tuo agio con il concetto. ""Umano"" vuole sempre prima fare qualcosa di produttivo, dopotutto. E scrivere test non sembra produttivo a prima vista. Provalo. A volte diventi amico di una nuova tecnica solo dopo averla conosciuta!Nei progetti con un'elevata copertura di test, mi sento più a mio agio quando aggiungo nuove funzionalità.

Se segui la parte dell'esercizio alla fine del testo, puoi provarlo. Prima crea il test e poi scrivi il codice per Joomla Core. Quindi invia tutto insieme come PR su Github. Se tutti lo facessero , Joomla avrebbe una copertura di test ideale.

Sviluppo guidato dal comportamento (BDD)

BDD non è un'altra tecnica di programmazione o tecnica di test, ma una sorta di best practice per lo sviluppo del software. BDD è idealmente utilizzato insieme a TDD. In linea di principio, Behaviour-Driven-Development sta per testare non l'implementazione del codice del programma, ma l'esecuzione - cioè il comportamento del programma Un test controlla se la specifica, cioè il requisito del cliente, è soddisfatta.

Quando sviluppi software in modo guidato dal comportamento, i test non solo ti aiutano a scrivere correttamente il programma, ma ti aiutano anche a scrivere il programma giusto .

Cosa intendo con questo: "Scrivi il programma giusto"? Succede che gli utenti vedano le cose in modo diverso rispetto agli sviluppatori. Il flusso di lavoro di eliminazione di un articolo in Joomla ne è un esempio. Ancora e ancora incontro utenti che fanno clic sull'icona di stato nel cestino e sono sorpresi.L'utente di solito presuppone intuitivamente che l'elemento sia ora eliminato in modo permanente, ma è passato da cestinato ad attivato.Per lo sviluppatore, fare clic sull'icona di stato è un cambiamento di stato, un interruttore.in tutte le altre visualizzazioni. Perché dovrebbe essere diverso nel cestino? Per lo sviluppatore, la funzione è implementata senza errori. Joomla funziona correttamente. Ma ai miei occhi la funzione non è quella giusta in quel punto, perché la maggior parte degli utenti la descriverebbe/richiederebbe in modo abbastanza diverso .

In Behavior Driven Development, i requisiti per il software sono descritti attraverso esempi chiamati scenari o storie utente.

  • un forte coinvolgimento dell'utente finale nel processo di sviluppo del software,
  • la documentazione di tutte le fasi del progetto con storie degli utenti/esempi di casi in forma di testo - di solito nel linguaggio descrittivo nel linguaggio descrittivo Gherkin,
  • test automatico di queste storie degli utenti/casi di studio,
  • successiva implementazione.In questo modo, è possibile accedere in qualsiasi momento a una descrizione del software da implementare.Con l'aiuto di questa descrizione, è possibile garantire continuamente la correttezza del codice del programma già implementato.

Il progetto Joomla ha introdotto BDD in un progetto Google Summer of Code . Si sperava che gli utenti senza conoscenze di programmazione sarebbero stati in grado di partecipare più facilmente utilizzando Gherkin ). L'approccio non è stato seguito in modo coerente. A quel tempo, Joomla utilizzava Codeception come strumento di test.Con Cypress, lo sviluppo BDD è anche possibile sviluppare in modo BDD.

Pianificazione

Tipi di prova
  • Test unitari: un test unitario è un test che verifica le unità di programma più piccole in modo indipendente.
  • Test di integrazione: un test di integrazione è un test che verifica l'interazione delle singole unità.
  • Test E2E o test di accettazione: un test di accettazione controlla se il programma soddisfa l'attività definita all'inizio.
Strategie

Se vuoi aggiungere una nuova funzione in Joomla e metterla al sicuro con dei test, puoi procedere in due modi.

Top-down e bottom-up sono due approcci fondamentalmente diversi per comprendere e presentare questioni complesse. Top-down va passo dopo passo dall'astratto e generale al concreto e specifico. Per illustrare questo con un esempio: un sistema di gestione dei contenuti come Joomla generalmente presenta i siti Web in un browser, ma concretamente ci sono una serie di piccole attività secondarie in questo processo, una delle quali è il compito di visualizzare un testo specifico in un'intestazione.

Bottom-up descrive la direzione opposta: a questo punto vale la pena ricordare ancora una volta che un elemento dello sviluppo behavior-driven è la creazione di una descrizione testuale del comportamento del software.Questa descrizione dei criteri di accettazione aiuta a creare dei test - specialmente il top test end-to-end o test di accettazione.

L'approccio abituale alla creazione di test oggi è dal basso. Se preferisci lo sviluppo di software basato sul comportamento, dovresti usare la strategia opposta. Dovresti usare la strategia dall'alto verso il basso. Con una strategia dall'alto verso il basso, un malinteso viene identificato nella fase iniziale in fase di progettazione.

Strategie di test: test top-down e test bottom-up

  • Test top-down: quando si applica la strategia top-down, si inizia con i test di accettazione, ovvero con la parte del sistema più strettamente legata ai requisiti dell'utente.Per il software scritto per utenti umani, questa è solitamente l'interfaccia utente . L'obiettivo è testare come un utente interagisce con il sistema. Uno svantaggio del test top-down è che occorre dedicare molto tempo alla creazione di duplicati di test. I componenti che non sono ancora integrati devono essere sostituiti da segnaposto. All'inizio non c'è un vero codice di programma, quindi le parti mancanti devono essere create artificialmente e, gradualmente, questi dati artificiali vengono poi sostituiti da dati realmente calcolati.

  • Test dal basso verso l'alto: se segui la strategia dal basso verso l'alto, inizi con test unitari. All'inizio, lo sviluppatore ha in mente lo stato target. Tuttavia, prima suddivide questo target in singoli componenti. Il problema con il L'approccio dal basso verso l'alto è che è difficile testare come un componente verrà successivamente utilizzato in situazioni reali.Il vantaggio del test dal basso verso l'alto è che abbiamo completato le parti del software molto rapidamente.Tuttavia, queste parti dovrebbero essere utilizzate con cautela. Funzionano correttamente, questo è ciò che assicurano i test unitari, ma non è garantito se il risultato finale sia davvero ciò che il cliente immagina che sia il software.

La piramide di prova di Mike Cohn

Quanti test devono essere implementati di quale tipo di test? La piramide dei test di Mike Cohn descrive un concetto per l'impiego dei test software automatizzati. La piramide è composta da tre livelli, strutturati in base alla frequenza di utilizzo e alla rilevanza. ‍

Idealmente, la base della piramide dei test è formata da molti test unitari rapidi e di facile manutenzione, in modo che la maggior parte degli errori possa essere rilevata rapidamente.

Al livello medio ci sono gli integration test, forniscono servizi per il test mirato di interfacce critiche, i tempi di esecuzione degli integration test sono più lunghi e la loro manutenzione è anche più complessa di quella degli unit test.

La parte superiore della piramide è costituita dai lenti test E2E, che a volte richiedono molta manutenzione.I test E2E sono molto utili per testare l'applicazione come sistema completo.

Requisiti

Di quale attrezzatura hai bisogno per lavorare sulla seguente parte pratica?

Quali requisiti hai per lavorare attivamente sulla seguente parte pratica?Non devi soddisfare molti requisiti per poter lavorare sui contenuti di questo manuale.Ovviamente, devi avere un computer.Un ambiente di sviluppo con Git, NodeJS e Composer e un server Web locale dovrebbero essere installati o installabili su di esso.

Quali conoscenze dovresti avere personalmente?

Dovresti conoscere le tecniche di programmazione di base, idealmente hai già programmato una piccola applicazione Web. In ogni caso, dovresti sapere dove archiviare i file sul tuo computer di sviluppo e come caricarli nel tuo browser Internet.

Provalo. Integra i test nel tuo prossimo progetto. Forse la tua prima esperienza di test ti farà risparmiare una noiosa sessione di debug o un bug imbarazzante nel sistema reale. Dopotutto, con una rete di sicurezza di test, puoi sviluppare software con meno fatica.

Impostare

Configurare Cypress con Joomla!

Nella versione per sviluppatori disponibile su Github, Joomla è Cypress ready configurato.Ci sono già dei test che puoi utilizzare come guida.Quindi non è necessario configurare tutto da solo per avere una prima panoramica.In questo modo, puoi sperimentare con Cypress , scopri i suoi vantaggi e svantaggi e decidi tu stesso se vuoi utilizzare lo strumento di test.

Passaggi per configurare l'ambiente locale:

Clona il repository nella root del tuo server web locale:

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

Passare alla cartella joomla-cms:

$ cd joomla-cms

Secondo Joomla Roadmap, la prossima versione principale 5.0 verrà rilasciata nell'ottobre 2023. Per essere aggiornato, utilizzo questa versione di sviluppo qui.

Passare al ramo 5.0-dev :

$ git checkout 5.0-dev

Installa tutti i pacchetti di compositore necessari:

$ composer install

Installa tutti i pacchetti npm necessari:

$ npm install

Per ulteriori informazioni e assistenza sulla configurazione della workstation, vedere l'articolo della documentazione di Joomla "Impostazione della workstation per lo sviluppo di Joomla" . Per Cypress, ci sono informazioni su cypress.io . Ma a questo punto non è necessario. Joomla imposta tutto Devi solo impostare i tuoi dati personali tramite il file di configurazione joomla-cms/cypress.config.js.

Imposta i tuoi dati individuali.Per questo puoi usare il modello joomla-cms/cypress.config.dist.jscome orientamento.Nel mio caso questo file ha questo aspetto:

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: Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Concretamente ho aggiunto la directory tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}all'Array specPattern , perché voglio salvare lì i test per i moduli in un secondo momento.Poi ho cambiato il nome utente e le password perché voglio anche testare l'installazione manualmente e ricordare meglio quelle autoassegnate.Uso un contenitore Docker come database, quindi ho cambiato il server del database ei dati di accesso e infine ho dovuto impostare l'URL di root http://localhost/joomla-cmsdella mia installazione di Joomla.

Usa il cipresso

Tramite browser web

Chiama npm run cypress:opentramite CLI nella tua directory principale di Joomla.Poco tempo dopo si aprirà l'app Cypress.Abbiamo precedentemente creato il file joomla-cms/cypress.config.dist.js.Che questo venga rilevato può essere visto dal fatto che E2E Testing è specificato come configurato.

L'app Cypress si apre dopo aver chiamato 96;npm esegui cypress:open96;.

Qui puoi scegliere se vuoi eseguire i test E2E e quale browser vuoi utilizzare.Per l'esempio, ho scelto l'opzione "Avvia test in Firefox".

Test E2E nell'app Cypress: selezionare il browser da utilizzare.

Verranno elencate tutte le suite di test disponibili e potrai fare clic su quella che desideri eseguire.Quando selezioni una suite di test, i test verranno eseguiti e potrai visualizzare l'esecuzione dei test in tempo reale nel browser.

Suite di test Joomla in Firefox tramite l'app Cypress.

Mentre i test sono in esecuzione, puoi vedere lo script eseguito da un lato e il risultato nel browser sul lato destro.Questi non sono solo screenshot, ma istantanee reali del browser in quel momento, quindi puoi vedere il codice HTML effettivo Sono anche possibili schermate e persino video dei test.

Test di installazione di Joomla in corso.

Provalo Se usi as db_host: 'localhost',puoi testare l'installazione e quindi aver configurato correttamente Joomla per il lavoro nella parte seguente di questo testo.

Se tu, come me, usi una fonte esterna (non lcoalhost; io uso un contenitore docker) come db_host, il test per questo tipo di installazione non è ancora pronto.In tal caso c'è una domanda per la sicurezza nella routine di installazione, che è non ancora considerato nei test. In questo caso, installa Joomla manualmente con le informazioni inserite nel file joomla-cms/cypress.config.js. I test successivi utilizzeranno le impostazioni di questo file di configurazione, ad esempio per l'accesso all'area di amministrazione di Joomla. In questo modo lo sviluppatore del test non non devi preoccuparti di inserire i dati di accesso.L'utente e la password corrispondenti vengono sempre utilizzati automaticamente dal file di configurazione.

Senza testa

Per impostazione predefinita, cypress runesegue tutti i test headless .Il seguente comando esegue tutti i test già codificati e salva gli screenshot nella directory /joomla-cms/tests/cypress/output/screenshotsin caso di errore.La directory di output è stata impostata nel cypress.config.jsfile.

$ npm run cypress:run

Altri comandi CLI

Ci sono altri comandi utili che non sono implementati come script nel package.jsonprogetto Joomla, li eseguo tramite npx [docs.npmjs.com/commands/npx].

verifica del cipresso

Il cypress verifycomando verifica che Cypress sia installato correttamente e possa essere eseguito.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
informazioni sul cipresso

Il cypress infocomando genera informazioni su Cypress e sull'ambiente corrente.

$ 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
versione cipresso

Il cypress versioncomando stampa la versione binaria di Cypress installata, la versione del pacchetto Cypress, la versione di Electron utilizzata per creare Cypress e la versione del nodo in bundle.

$ 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 documentazione di Cypress fornisce informazioni più dettagliate.

Scrivere il primo proprio test

Se tutto ha funzionato finora, possiamo iniziare a creare i nostri test.

Ottieni una panoramica

Imparare dai test già sviluppati

Nella versione di sviluppo del CMS Joomla sono già presenti i test Cypress, che si trovano nella cartella /tests/System/integrationChi ama imparare con gli esempi, qui troverà un'adeguata introduzione.

Codice di importazione per attività ripetitive

Gli sviluppatori di Joomla stanno lavorando al progetto NodeJs joomla-cypress , che fornisce codice di test per casi di test comuni che vengono importati durante l'installazione della versione per sviluppatori del CMS npm installtramite

  • package.jsone tramite il
  • file di supporto /tests/System/support/index.jsIl file di supporto è definito nella configurazione 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 esempio è il clic su un pulsante della barra degli strumenti.Ad esempio, Cypress.Commands.add('clickToolbarButton', clickToolbarButton)rende disponibile il comando clickToolbarButton()nei test personalizzati e tramite cy.clickToolbarButton('new')un clic sul pulsante Newviene simulato.Il codice necessario per questo è mostrato nel codenipped di seguito.

// 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)
...

Il codice seguente mostra un altro esempio, il login all'area di amministrazione.

// /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)

...
Compiti comuni nel singolo ambiente

Nella directory /tests/System/supportsi trovano le attività comuni nel singolo ambiente, per poterle riutilizzare facilmente vengono importate tramite il file di supporto /tests/System/support/index.jsUn esempio di attività ripetuta frequentemente è il login all'area di amministrazione, che viene gestito nel file /tests/System/support/commands.jsutilizzando la funzione doAdministratorLogin.

Il codice seguente mostra anche come vengono utilizzate le informazioni dalla cypress.config.jsconfigurazione nei test. Cypress.env('username')viene assegnato il valore della usernameproprietà all'interno del gruppo env.

Inoltre, possiamo vedere qui come sovrascrivere i comandi Cypress.Commands.overwrite('doAdministratorLogin' ...),sovrascrive il codice che abbiamo appena visto nel pacchetto joomla-cypressIl vantaggio è che utente e password vengono utilizzati automaticamente dalla configurazione individuale.

// /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 });
});
...

Installa la tua estensione Joomla

Per vedere come testare il proprio codice, installeremo un semplice componente di esempio tramite il backend di Joomla.Il file per l'installazione può essere scaricato da Codeberg .

Installa la tua estensione Joomla.

Dopo l'installazione, puoi trovare un collegamento alla visualizzazione del componente Foo nella barra laterale sinistra del backend di Joomla.

Vista del componente di esempio nel backend di Joomla.

Ora abbiamo un ambiente di test impostato e il codice per il test.

Il primo proprio test

Ganci

Durante il test del backend, noterai che devi iniziare ogni test con un login. Possiamo evitare questo codice ridondante utilizzando la beforeEach()funzione. Questo cosiddetto hook esegue il codice che inseriamo prima dell'esecuzione di ogni test. Da qui il nome beforeEach().

Cypress fornisce diversi tipi di hook , inclusi beforee afterhook che vengono eseguiti prima o dopo i test in un gruppo di test e beforeEachhook afterEachche vengono eseguiti prima o dopo ogni singolo test nel gruppo. Gli hook possono essere definiti globalmente o all'interno di un describedblocco specifico. esempio di codice nel file tests/System/integration/administrator/components/com_foos/FoosList.cy.jsfa sì che venga eseguito un login nel back-end prima di ogni test all'interno del describedblock test com_foos features.

Ora iniziamo con la parte pratica e creiamo il file tests/System/integration/administrator/components/com_foos/FoosList.cy.jscon i codici successivi prima di scrivere il primo test produttivo.Il nostro primo esempio dovrebbe registrarci correttamente nel backend prima di qualsiasi test!Lo testeremo dopo aver creato il primo test.

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

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

})

Nota: gli hook, implementati all'interno di file /tests/System/support/index.js, si applicano a ciascun file di test nella tuta di test.

Una prova riuscita

Il componente che abbiamo installato per il test contiene i tre elementi Astrid, Ninae Elmar.In primo luogo, testiamo se questi elementi sono stati creati correttamente.

// 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 funzione checkForPhpNoticesOrWarnings()che trovi nel file /node_modules/joomla-cypress/src/support.js.

Otteniamo l'elemento DOM maintramite il comando Cypress get

Dovresti trovare il tuo test appena creato FooList.cy.jsnell'elenco dei test disponibili nella barra laterale di sinistra.In caso contrario, chiudi il browser ed eseguilo npm run cypress:opendi nuovo.

Joomla esegue il test per la propria estensione.

Clicca sul nome del test per eseguirlo, dovrebbe concludersi con successo e vedrai dei messaggi verdi.

La visualizzazione dopo che il test è stato eseguito correttamente.

Una prova fallita

Aggiungi la riga cy.get('main').should('contain.text', 'Sami')al file di test in modo che l'esecuzione fallisca.Non ci sono elementi con questo nome.Dopo aver salvato il file di test, Cypress nota la modifica.Dopo ogni modifica, Cypress riesegue automaticamente tutti i test nel file di test.

// 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()
  })
})

Come previsto, il test fallisce. Ci sono messaggi rossi. Puoi vedere il codice di ogni passo del test nella barra laterale sinistra. Quindi è possibile trovare il motivo dell'errore. Per ogni passo c'è un'istantanea del documento HTML, così puoi controllare il markup in qualsiasi momento, il che è utile, specialmente durante lo sviluppo.

La vista dopo il test ha fallito.

Eseguire un solo test in un file

La nostra estensione demo contiene più di un layout. Aggiungi un test per testare il layout dello stato vuoto. Poiché ora abbiamo due test in questo file, Cypress eseguirà sempre entrambi i test ogni volta che salviamo il file. Possiamo utilizzare in modo che solo un .only()test viene eseguito:

// 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 lo sviluppo, questo è molto conveniente.

Attributi di test speciali

Ora ci piace testare il frontend per il nostro componente, lo facciamo in un file separato /tests/System/integration/site/components/com_foos/FooItem.cy.js.

La maggior parte delle volte usiamo una classe CSS per ottenere un elemento nei test di Joomla. Anche se questo è perfettamente valido e funzionerà, in realtà non è raccomandato. Perché no? Quando usi classi o ID CSS, leghi i tuoi test a cose che molto probabilmente cambierà nel tempo. Le classi e gli ID servono per il design, il layout e talvolta tramite JavaScript per il controllo, che può cambiare facilmente. Se qualcuno cambia il nome o l'ID di una classe, i tuoi test non funzioneranno più. Per rendere i tuoi test meno fragili e più a prova di futuro, Cypress consiglia di creare attributi di dati speciali per i tuoi elementi appositamente per scopi di test.

Userò l' data-testattributo per gli elementi.Per prima cosa aggiungo l'attributo data-test="foo-main"al codice di produzione.

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

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

Quindi collaudo il codice di produzione cercando l'attributo [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()
  })
})
Test di una voce di menu e alcune riflessioni su eventi, attese e best practice

Ora mi piace provare la creazione di una voce di menu per il nostro componente.Lo faccio in un file separato.Questo /tests/System/integration/administrator/components/com_foos/MenuItem.cy.jscodice è complesso e mostra molte caratteristiche speciali.

Innanzitutto, ho definito una costante in cui ho impostato tutte le proprietà rilevanti della voce di menu.Questo ha il vantaggio che in caso di modifiche di una proprietà rilevante devo regolare solo in un posto:

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

Successivamente viene visualizzato l'intero codice del file 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')
  })
})
  • In questo codice puoi vedere un esempio di testare qualcosa e poi cancellare tutto - ripristinando così lo stato iniziale. In questo modo puoi ripetere i test tutte le volte che vuoi. Senza ripristinare lo stato iniziale, la seconda esecuzione del test fallirà perché Joomla non può memorizzare due elementi simili.

Nota: un test dovrebbe essere:

  • ripetibile.
  • mantenuto semplice In termini concreti, ciò significa che dovrebbe testare un problema limitato e il codice per questo non dovrebbe essere troppo esteso.
  • indipendente da altri esami.
  • E puoi vedere come utilizzare un percorso intercettato definito con cy.intercept()[^docs.cypress.io/api/commands/intercept] come alias, e quindi attendere il percorso definito come alias con cy.wait().

Quando si scrivono test per tali applicazioni, si è tentati di utilizzare valori casuali come cy.wait(2000);nel cy.waitcomando.Il problema con questo approccio è che mentre questo può funzionare bene in fase di sviluppo.Tuttavia, non è garantito che funzioni sempre.Perché?Perché il sistema sottostante dipende da cose difficili da prevedere, quindi è sempre meglio definire esattamente cosa stai aspettando.

  • Il codice mostra anche come attendere un avviso e confermarlo .
cy.on("window:confirm", (s) => {
  return true;
});
  • Infine, ma non meno importante, il codice di test contiene il build in di Cypress e le funzioni tipiche di Joomla che possono essere riutilizzate dagli sviluppatori di estensioni, ad esempio, cy.setFilter('published', 'Trashed')oppure cy.clickToolbarButton('Save & Close')sono funzioni in cui si possono trovare soluzioni per i singoli test in generale e di cui spesso hanno bisogno gli sviluppatori di Joomla in particolare .
Combinazione di codice asincrono e di sincronizzazione

I comandi di Cypress sono asincroni, cioè non restituiscono un valore, ma generateesso.Quando avviamo Cypress, non esegue i comandi immediatamente, ma li legge in serie e li mette in coda.Se mescoli codice asincrono e sincrono nei test, tu potrebbe ottenere risultati inaspettati.Se esegui il seguente codice, otterrai un errore contro le aspettative.Sicuramente ti saresti anche aspettato che mainText = $main.text()cambi il valore mainText.Ma mainText === 'Initial'è ancora valido alla fine.Perché? Cypress esegue prima il codice sincrono a all'inizio e alla fine. Solo allora chiama la parte asincrona all'interno di then().Ciò significa che la variabile mainTextviene inizializzata e subito dopo viene verificata se è cambiata, il che ovviamente non è il 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}`);
}

L'elaborazione della coda diventa abbastanza chiara e visiva se si osserva l'esecuzione del seguente codice nella console del browser.Il testo 'Cypress Test.' appare molto prima che venga mostrato il contenuto dell'elemento, sebbene le righe di codice mainsiano in un ordine diverso.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Stub e spie

A stubè un modo per simulare il comportamento della funzione da cui dipendono i test.Invece di chiamare la funzione effettiva, lo stub sostituisce quella funzione e restituisce un oggetto predefinito.Di solito è usato negli unit test, ma può anche essere usato per end -to-end test.

A spyè simile a stub, ma non esattamente uguale. Non modifica il comportamento di una funzione, ma la lascia così com'è. Cattura alcune informazioni su come viene chiamata la funzione. Ad esempio, per verificare se la funzione viene chiamata con i parametri corretti o per contare la frequenza con cui viene chiamata la funzione.

L'esempio seguente mostra a spye a stubin azione. Via const stub = cy.stub()creiamo l' stubelemento e determiniamo nel passo successivo che falseviene restituito per la prima chiamata e trueper la seconda. Usando cy.on('window:confirm', stub)facciamo in modo che stubsia usato per window:confirm'. Nel passo successivo creiamo con cy.spy(win, 'confirm').as('winConfirmSpy')l' Spyelemento , che osserva la chiamata di 'window:confirm'. Ora testiamo che alla prima chiamata la cancellazione della categoria viene rifiutata e alla seconda chiamata viene confermata. In tal modo, assicura stubche possiamo aspettarci con certezza quali saranno i valori di ritorno consegnato 'window:confirm'è incapsulato @winConfirmSpyaiuta a garantire che la funzione sia stata effettivamente chiamata e la frequenza con cui è stata chiamata.

// 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')
...

Se si tratta solo di impostare un valore fisso per la 'window:confirm'chiamata, il codice seguente farà il lavoro.

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

conclusione

In questo articolo hai visto la teoria di base e le caratteristiche pratiche dei test E2E con Cypress. Ho utilizzato l'installazione di Joomla per dimostrare come scrivere diversi test per garantire che un componente Joomla su un sito Web funzioni come previsto. Ho anche mostrato come personalizzare il Cypress Test Runner nel file cypress.json e come utilizzare i comandi personalizzati di Cypress, utilizzando esempi facili da seguire.

Spero che ti sia piaciuto il tour attraverso Cypress usando Joomla come esempio e che tu sia stato in grado di portare via molta conoscenza e ispirazione per te stesso.

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. 

Si prega di notare che questo sito Web utilizza un sistema di traduzione automatica per aiutare nella traduzione per le diverse lingue.Ci scusiamo per qualsiasi errore o errore di battitura che può essere mostrato nei diversi testi.