Geautomatiseerde tests zijn geen speciale tool voor softwareontwikkelaars in grote projecten. Vooral voor extensies zijn geautomatiseerde tests een hulpmiddel om snel problemen te identificeren. Ze helpen ervoor te zorgen dat extensies soepel werken in nieuwere Joomla-versies.
De Joomla Core-ontwikkelaars willen dat externe softwareontwikkelaars hun extensies testen, om bugs te vinden voordat een gebruiker ze vindt. Dit kost veel tijd en is saai werk. Daarom wordt het vaak niet gedaan. Zeker niet als het moet. handmatig door mensen voor elke nieuwe release. Geautomatiseerd testen maakt het mogelijk om de handmatige stappen voor elke release te herhalen zonder dat een mens de stappen zelf uitvoert. Op deze manier worden bugs gevonden voordat een gebruiker ze tegenkomt bij toegang tot het live systeem.
Trouwens, iedereen die Cypress eens wil bekijken, vindt in deze tekst een goed startpunt. Je kunt problemen testen zonder zelf alles te hoeven installeren en configureren. In de Github-repository van het Joomla-project is alles klaar voor gebruik.
Inleiding
"Hoewel het waar is dat kwaliteit niet kan worden getest, is het even duidelijk dat het zonder testen onmogelijk is om iets van kwaliteit te ontwikkelen." - [James A. Whittaker]
Voordat ik Cypress voor het eerst tegenkwam, had ik me niet kunnen voorstellen dat de barrières die mijn testen vaak in de weg stonden, eigenlijk een beetje opzij zouden worden geschoven. Ik besteedde veel tijd aan het testen van software - en eerder nog meer tijd aan het oplossen van de problemen dat is ontstaan door een gebrek aan testen!Nu ben ik ervan overtuigd dat testen die zijn:
- zo dicht mogelijk bij de programmering,
- automatisch,
- vaak - idealiter na elke programmawijziging
meer binnenhalen dan ze kosten En bovendien: testen kan zelfs leuk zijn.
Het is de moeite waard om testmethoden te leren! Testmethoden gaan lang mee, omdat ze niet alleen met elke programmeertaal kunnen worden gebruikt, ze kunnen op bijna elk menselijk werk worden toegepast. Je zou bijna alles wat belangrijk is in het leven af en toe moeten testen. Testmethoden zijn onafhankelijk van specifieke softwaretools.In tegenstelling tot programmeertechnieken of programmeertalen, die vaak in en uit de mode zijn, is de kennis van het opzetten van goede tests tijdloos.
Wie zou deze tekst moeten lezen?
Iedereen die denkt dat het testen van software tijdverspilling is, zou dit artikel eens moeten bekijken. Ik wil in het bijzonder die ontwikkelaars uitnodigen om dit te lezen die altijd al tests voor hun software hebben willen schrijven - maar nog nooit voor een variëteit hebben gedaan. Cypress zou een manier kunnen zijn om dergelijke barrières weg te nemen.
Wat theorie
De magische driehoek
De Magische Driehoek beschrijft de relatie tussen de kosten, de benodigde tijd en de haalbare kwaliteit. Oorspronkelijk werd deze relatie onderkend en beschreven in projectmanagement. Maar waarschijnlijk heb je ook op andere gebieden van dit spanningsveld gehoord. processen in een bedrijf.
Zo wordt algemeen aangenomen dat hogere kosten een positieve invloed hebben op de kwaliteit en/of de opleverdatum, oftewel de tijd.
Andersom zal een kostenbesparing de kwaliteit doen afnemen en/of de oplevering vertragen.
Nu komt de magie om de hoek kijken: we overwinnen de correlatie tussen tijd, kosten en kwaliteit, want op de lange termijn kan dit echt worden overwonnen.
Het verband tussen tijd, kosten en kwaliteit kan op de lange termijn worden overwonnen.
Wellicht heeft ook u in de praktijk ervaren dat kwaliteitsvermindering op de lange termijn niet leidt tot kostenbesparingen, de technische schuld die hierdoor ontstaat leidt vaak zelfs tot kostenstijgingen en extra tijd.
Technische schuld verwijst naar de extra inspanning die nodig is om wijzigingen en verbeteringen aan te brengen in niet goed geprogrammeerde software in vergelijking met goed geschreven software. Martin Fowler onderscheidt de volgende soorten technische schuld: schulden die men opzettelijk is aangegaan en schulden die men per ongeluk is aangegaan Ook maakt hij onderscheid tussen voorzichtige en roekeloze technische schulden.
Kosten en baten
In de literatuur vind je vernietigende statistieken over de slaagkansen van softwareprojecten.Er is weinig veranderd aan het negatieve beeld dat al in een studie van AW Feyhl in de jaren 90 opgetekend werd.Hier in een analyse van 162 projecten in 50 organisaties , De kostenafwijking ten opzichte van de oorspronkelijke planning werd vastgesteld: 70% van de projecten vertoonde een kostenafwijking van minimaal 50%! Er klopt iets niet! Dat kun je toch niet zomaar accepteren?
Een oplossing zou zijn om helemaal af te zien van kostenramingen en de argumentatie van de #NoEstimates-beweging te volgen. Deze stroming is van mening dat kostenramingen in een softwareproject onzinnig zijn. Een softwareproject bevat volgens #NoEstimates altijd de productie van iets nieuws Het nieuwe is niet vergelijkbaar met reeds bestaande ervaringen en dus niet voorspelbaar.
Hoe meer ervaring ik opdoe, hoe meer ik tot de conclusie kwam dat extreme opvattingen niet goed zijn. De oplossing ligt bijna altijd in het midden. Vermijd ook in softwareprojecten extremen en zoek een midden. 't hoeft geen 100% te zijn. zeker plan. Maar je moet ook niet naïef aan een nieuw project beginnen. Hoewel softwareprojectbeheer en vooral kostenraming een belangrijk onderwerp is, zal ik je er in deze tekst niet langer mee vervelen. De focus van dit artikel is om te laten zien hoe E2E testen kan worden geïntegreerd in de praktische workflow van softwareontwikkeling.
Integreer softwaretesten in uw workflow
U heeft besloten om uw software te testen. Geweldig! Wanneer is de beste tijd om dit te doen? Laten we eens kijken naar de kosten van het oplossen van een bug in de verschillende projectfasen. Hoe eerder u een bug vindt, hoe lager de kosten om deze te repareren .
Testen en Debuggen: Er zijn woorden die vaak in één adem worden genoemd en waarvan de betekenis dus gelijk is. Bij nader inzien staan de termen echter voor verschillende interpretaties. Testen en debuggen horen bij deze woorden. De twee termen hebben gemeen dat ze storingen opsporen, maar er zijn ook verschillen in de betekenis.
- Tests vinden onbekende storingen tijdens de ontwikkeling.Het vinden van de storing is en duur, terwijl het lokaliseren en verhelpen van de fout goedkoop is.
- Debuggers repareren storingen die worden gevonden nadat het product is voltooid.Het vinden van de storing is gratis, maar het lokaliseren en oplossen van de fout is duur.
Conclusie: Het is het meest logisch om zo vroeg mogelijk te beginnen met het integreren van tests, maar helaas is dit moeilijk te implementeren in een open source project als Joomla met voornamelijk vrijwillige bijdragers.
Continue integratie (CI)
Continue integratie van testen
Stel je het volgende scenario voor. Er komt binnenkort een nieuwe versie van een populair contentmanagementsysteem uit. Alles wat de ontwikkelaars van het team sinds de laatste release hebben bijgedragen, wordt nu voor het eerst samen gebruikt. De spanning stijgt! werk? Zullen alle tests slagen - als het project überhaupt tests integreert. Of moet de release van de nieuwe versie opnieuw worden uitgesteld en staan er zenuwslopende uren van bugfixing in het verschiet? Overigens is het uitstellen van de releasedatum ook niet goed voor het imago van het softwareproduct! Geen enkele ontwikkelaar maakt dit scenario graag mee. Veel beter is het om op elk moment te weten in welke staat het softwareproject zich momenteel bevindt? Code die niet past in het bestaande moet pas achteraf worden geïntegreerd ze zijn "op maat gemaakt".Vooral in tijden waarin het steeds vaker voorkomt dat een beveiligingslek moet worden gedicht, moet een project altijd een release kunnen creëren!En hier komt continue integratie om de hoek kijken.
Bij continue integratie worden individuele elementen van de software permanent geïntegreerd. De software wordt in kleine cycli gemaakt en getest. Op deze manier stuit je in een vroeg stadium op problemen bij de integratie of foutieve tests en niet dagen of weken later. Bij een succesvolle integratie, Het oplossen van problemen is veel eenvoudiger omdat fouten worden ontdekt vlak voor het programmeren en meestal slechts een klein deel van het programma wordt beïnvloed. Joomla integreert nieuwe code met behulp van continue integratie. Nieuwe code wordt alleen geïntegreerd wanneer alle tests zijn geslaagd.
Met een continue integratie van nieuwe software is het oplossen van problemen veel eenvoudiger omdat de fouten worden ontdekt vlak voor het programmeren en meestal is slechts een klein deel van het programma aangetast.
Om er zeker van te zijn dat u tijdens continue integratie altijd over alle programmaonderdelen beschikt, moet u testgestuurde software ontwikkelen.
Testgestuurde ontwikkeling (TDD)
Testgestuurd ontwikkelen is een programmeertechniek waarbij in kleine stapjes wordt ontwikkeld. Eerst schrijf je de testcode. Pas daarna maak je de te testen programmacode aan. Elke wijziging aan het programma wordt pas doorgevoerd nadat de testcode voor die wijziging is zijn gemaakt. Uw tests mislukken dus meteen na het maken. De vereiste functie is nog niet geïmplementeerd in het programma. Pas dan maakt u de eigenlijke programmacode aan - dat wil zeggen, de programmacode die aan de test voldoet.
De TDD-testen helpen u om het programma correct te schrijven .
Wanneer je voor het eerst over deze techniek hoort, voel je je misschien niet op je gemak bij het concept. ""Mens"" wil immers altijd eerst iets productiefs doen. En het schrijven van toetsen lijkt op het eerste gezicht niet productief. Probeer het eens. Soms je raakt pas bevriend met een nieuwe techniek nadat je deze hebt leren kennen!In projecten met een hoge testdekking voel ik me meer op mijn gemak als ik nieuwe functies toevoeg.
Als je het oefeningsgedeelte aan het einde van de tekst doorloopt, kun je het uitproberen. Eerst de test maken en dan de code voor Joomla Core schrijven. Daarna alles samen als PR op Github indienen. Als iedereen dit zou doen, zou Joomla zou een ideale testdekking hebben.
Gedragsgestuurde Ontwikkeling (BDD)
BDD is geen andere programmeertechniek of testtechniek, maar een soort best practice voor softwareontwikkeling. BDD wordt idealiter samen met TDD gebruikt. In principe staat Behaviour-Driven-Development voor het testen van niet de implementatie van de programmacode, maar de uitvoering - dwz het gedrag van het programma Een test controleert of aan de specificatie, dwz de eis van de klant, wordt voldaan.
Wanneer je software ontwikkelt op een gedragsgestuurde manier, helpen testen je niet alleen om het programma correct te schrijven, testen helpen je ook om het juiste programma te schrijven .
Wat bedoel ik daarmee: "Schrijf het juiste programma"? Het komt voor dat gebruikers dingen anders zien dan ontwikkelaars. De workflow van het verwijderen van een artikel in Joomla is een voorbeeld. Steeds weer kom ik gebruikers tegen die op het statuspictogram klikken in de prullenbak en zijn verrast. De gebruiker gaat er meestal intuïtief van uit dat het item nu definitief is verwijderd, maar het is overgeschakeld van prullenbak naar activeren. Voor de ontwikkelaar is klikken op het statuspictogram een statuswijziging, een schakelaar. in alle andere weergaven. Waarom zou dit anders in de prullenbak moeten? Voor de ontwikkelaar wordt de functie foutloos geïmplementeerd. Joomla werkt correct. Maar in mijn ogen is de functie op die plek niet de juiste, omdat de meeste gebruikers het heel anders zouden omschrijven/vragen .
In Behaviour Driven Development worden de vereisten voor de software beschreven aan de hand van voorbeelden die scenario's of gebruikersverhalen worden genoemd.
- een sterke betrokkenheid van de eindgebruiker bij het ontwikkelingsproces van de software,
- de documentatie van alle projectfasen met user stories/case voorbeelden in tekstvorm - meestal in de beschrijvingstaal in de beschrijvingstaal Gherkin,
- automatisch testen van deze user stories/case studies,
- opeenvolgende implementatie.Zo kan op elk moment een beschrijving van de te implementeren software worden opgevraagd.Met behulp van deze beschrijving kunt u continu de juistheid van de reeds geïmplementeerde programmacode waarborgen.
Het Joomla-project heeft BDD geïntroduceerd in een Google Summer of Code-project . Men hoopte dat gebruikers zonder programmeerkennis gemakkelijker zouden kunnen deelnemen door Gherkin te gebruiken . De aanpak werd niet consistent opgevolgd. Destijds gebruikte Joomla Codeception als een testing tool Met Cypress is BDD development ook mogelijk om op de BDD manier te ontwikkelen.
Planning
Soorten testen
- Unit-tests: Een unit-test is een test die de kleinste programma-eenheden onafhankelijk test.
- Integratietesten: Een integratietest is een test die de interactie van individuele eenheden test.
- E2E-tests of acceptatietests: een acceptatietest controleert of het programma voldoet aan de taak die aan het begin is gedefinieerd.
Strategieën
Als u een nieuwe functie in Joomla wilt toevoegen en deze met tests wilt beveiligen, kunt u op twee manieren te werk gaan.
Top-down en bottom-up zijn twee fundamenteel verschillende benaderingen om complexe vraagstukken te begrijpen en te presenteren. Top-down gaat stap voor stap van het abstracte en algemene naar het concrete en specifieke. Ter illustratie met een voorbeeld: een content management systeem zoals Joomla presenteert meestal websites in een browser. Concreet zijn er echter een aantal kleine deeltaken in dit proces, waaronder het weergeven van een specifieke tekst in een kop.
Bottom-up beschrijft de tegenovergestelde richting: op dit punt is het de moeite waard om nogmaals te onthouden dat een element van gedragsgestuurde ontwikkeling het creëren van een tekstuele beschrijving van het gedrag van de software is. Deze beschrijving van acceptatiecriteria helpt bij het maken van tests - vooral de top -niveau end-to-end testen of acceptatietesten.
De gebruikelijke aanpak voor het maken van tests vandaag is van onderaf. Als u de voorkeur geeft aan gedragsgestuurde softwareontwikkeling, moet u de tegenovergestelde strategie gebruiken. U moet de top-downstrategie gebruiken. Met een top-downstrategie wordt een misverstand al vroeg opgemerkt in de ontwerpfase.
-
Top-down testen: Bij het toepassen van de top-down strategie begint men met de acceptatietesten - dat wil zeggen met het deel van het systeem dat het meest aansluit bij de gebruikerseisen.Voor software geschreven voor menselijke gebruikers is dit meestal de gebruikersinterface "De focus ligt op het testen hoe een gebruiker met het systeem omgaat. Een nadeel van top-down testen is dat er veel tijd moet worden besteed aan het maken van testduplicaten. Componenten die nog niet zijn geïntegreerd, moeten worden vervangen door placeholders. Daar is in het begin geen echte programmacode. Daarom moeten ontbrekende delen kunstmatig worden gecreëerd. Geleidelijk aan worden deze kunstmatige gegevens vervolgens vervangen door echt berekende gegevens.
-
Bottom-up testen: Als je de bottom-up strategie volgt, begin je met unit tests. In het begin heeft de ontwikkelaar de doelstatus in gedachten. Hij splitst dit doel echter eerst op in afzonderlijke componenten. Het probleem met de De bottom-up benadering is dat het moeilijk is om te testen hoe een component later in reële situaties zal worden gebruikt.Het voordeel van bottom-up testen is dat we heel snel softwareonderdelen af hebben.Deze onderdelen dienen echter met de nodige voorzichtigheid gebruikt te worden. Ze werken wel correct, dat is wat de unittests garanderen, maar of het eindresultaat ook echt is wat de klant zich van de software voorstelt, is niet gegarandeerd.
De testpiramide van Mike Cohn
Hoeveel tests moeten er van welk testtype worden uitgevoerd? De testpiramide van Mike Cohn beschrijft een concept voor de inzet van de geautomatiseerde softwaretests. De piramide bestaat uit drie niveaus, gestructureerd naar gebruiksfrequentie en relevantie.
Idealiter wordt de basis van de testpiramide gevormd door vele snelle en eenvoudig te onderhouden unittests, zodat de meeste fouten snel kunnen worden opgespoord.
Op het middelste niveau bevinden zich de integratietesten.Zij leveren diensten voor het gericht testen van kritieke interfaces.De uitvoeringstijden van integratietesten zijn langer en het onderhoud ervan is ook complexer dan dat van unittesten.
De top van de piramide bestaat uit trage E2E-testen, die soms veel onderhoud vergen.E2E-testen zijn erg handig om de applicatie als een compleet systeem te testen.
Vereisten
Welke apparatuur heb je nodig om aan het volgende praktijkonderdeel te werken?
Aan welke eisen moet je voldoen om actief aan het volgende praktijkgedeelte te werken? Je hoeft niet aan heel veel eisen te voldoen om aan de inhoud van deze handleiding te kunnen werken. Je hebt natuurlijk wel een computer nodig. Een ontwikkelomgeving met Git, NodeJS en Composer en een lokale webserver moeten erop zijn geïnstalleerd of kunnen worden geïnstalleerd.
Welke kennis moet je persoonlijk hebben?
Je moet de basistechnieken van programmeren kennen. Idealiter heb je al een kleine webapplicatie geprogrammeerd. Je moet in ieder geval weten waar je bestanden op je ontwikkelcomputer moet opslaan en hoe je ze in je internetbrowser moet laden. nieuwe dingen.
Probeer het uit. Integreer tests in uw volgende project. Misschien bespaart uw eerste ervaring met een test u een vervelende foutopsporingssessie of een gênante bug in het echte systeem. Met een vangnet van tests kunt u tenslotte software ontwikkelen met minder spanning.
Opzetten
Cypress opzetten met Joomla!
In de ontwikkelaarsversie beschikbaar op Github is Joomla Cypress ready geconfigureerd. Er zijn al testen die je als leidraad kunt gebruiken. Het is dus niet nodig om alles zelf in te stellen om een eerste overzicht te krijgen. Zo kan je experimenteren met Cypress , leer over de voor- en nadelen en beslis zelf of je de testtool wilt gebruiken.
Stappen om de lokale omgeving in te stellen:
Kloon de repository naar de root van uw lokale webserver:
$ git clone https://github.com/joomla/joomla-cms.git
Navigeer naar de joomla-cms map:
$ cd joomla-cms
Volgens Joomla Roadmap zal de volgende grote versie 5.0 in oktober 2023 worden uitgebracht. Om up-to-date te zijn, gebruik ik deze ontwikkelingsversie hier.
Ga naar de tak 5.0-dev :
$ git checkout 5.0-dev
Installeer alle benodigde componistenpakketten:
$ composer install
Installeer alle benodigde npm-pakketten:
$ npm install
Voor meer informatie en hulp bij het instellen van uw werkstation, zie het Joomla-documentatieartikel "Uw werkstation instellen voor Joomla-ontwikkeling" . Voor Cypress is er informatie op cypress.io . Maar dat is op dit moment niet nodig. Joomla stelt alles in U hoeft alleen uw individuele gegevens via het configuratiebestand in te stellen joomla-cms/cypress.config.js
.
Stel uw individuele gegevens in. Hiervoor kunt u de sjabloon joomla-cms/cypress.config.dist.js
als oriëntatie gebruiken. In mijn geval ziet dit bestand er als volgt uit:
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: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. ',
username: 'admin',
password: 'adminadminadmin',
db_type: 'MySQLi',
db_host: 'mysql',
db_name: 'test_joomla',
db_user: 'root',
db_password: 'root',
db_prefix: 'j4_',
},
})
Concreet heb ik de directory toegevoegd tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}
aan de specPattern
Array, omdat ik daar later test voor modules wil opslaan. Daarna heb ik de gebruikersnaam en wachtwoorden gewijzigd omdat ik de installatie ook handmatig wil testen en de zelf toegewezen beter wil onthouden. Ik gebruik een Docker-container als database.Daarom heb ik de databaseserver en de toegangsgegevens gewijzigd.En tot slot moest ik de root-URL http://localhost/joomla-cms
van mijn Joomla-installatie instellen.
Gebruik cipres
Via webbrowser
Aanroepen npm run cypress:open
via CLI in uw Joomla root directory. Korte tijd later opent de Cypress app. We hebben eerder het bestand gemaakt joomla-cms/cypress.config.dist.js
. Dat dit wordt gedetecteerd blijkt uit het feit dat E2E Testing is opgegeven als geconfigureerd.
Hier kun je kiezen of je de E2E-testen wilt uitvoeren en welke browser je wilt gebruiken.Voor het voorbeeld heb ik gekozen voor de optie "Start testen in Firefox".
Alle beschikbare testsuites worden weergegeven en u kunt op degene klikken die u wilt uitvoeren.Wanneer u een testsuite selecteert, worden de tests uitgevoerd en kunt u de uitvoering van de tests in realtime in de browser bekijken.
Terwijl de tests draaien, ziet u aan de ene kant het uitgevoerde script en aan de rechterkant het resultaat in de browser. Dit zijn niet zomaar screenshots, maar echte snapshots van de browser op dat moment, zodat u de daadwerkelijke HTML-code kunt zien Screenshots en zelfs video's van de tests zijn ook mogelijk.
Probeer het uit Als u gebruikt zoals db_host: 'localhost',
u de installatie kunt testen en Joomla dus correct hebt geconfigureerd voor het werk aan het volgende deel van deze tekst.
Als je, zoals ik, een externe bron gebruikt (niet lcoalhost; ik gebruik een docker-container) als db_host
, is de test voor dit soort installatie nog niet klaar. In dat geval is er een vraag naar beveiliging in de installatieroutine, en dat is nog niet meegenomen in de tests. Installeer in dit geval Joomla handmatig met de informatie die in het bestand is ingevoerd joomla-cms/cypress.config.js
. De volgende tests zullen de instellingen uit dit configuratiebestand gebruiken, bijvoorbeeld om in te loggen in het Joomla-beheergebied. Op deze manier doet de testontwikkelaar dat U hoeft zich geen zorgen te maken over het invoeren van de inloggegevens, de overeenkomende gebruiker en het wachtwoord worden altijd automatisch uit het configuratiebestand gebruikt.
Zonder hoofd
Voert standaard cypress run
alle tests zonder kop uit. De volgende opdracht voert alle reeds gecodeerde tests uit en slaat schermafbeeldingen op in de map /joomla-cms/tests/cypress/output/screenshots
in geval van een fout. De uitvoermap is ingesteld in het cypress.config.js
bestand.
$ npm run cypress:run
Andere CLI-opdrachten
Er zijn andere handige commando's die niet als scripts zijn geïmplementeerd in package.json
het Joomla-project, ik voer ze uit via npx [docs.npmjs.com/commands/npx].
cipres verifiëren
De cypress verify
opdracht verifieert dat Cypress correct is geïnstalleerd en kan worden uitgevoerd.
$ npx cypress verify
✔ Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
cipres info
De cypress info
opdracht voert informatie uit over Cypress en de huidige omgeving.
$ 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
cipres versie
De cypress version
opdracht drukt de geïnstalleerde binaire versie van Cypress af, de versie van het Cypress-pakket, de versie van Electron die is gebruikt om Cypress te maken en de versie van het gebundelde knooppunt.
$ 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
De documentatie van Cypress geeft meer gedetailleerde informatie.
Het schrijven van de eerste eigen Test
Als alles tot nu toe heeft gewerkt, kunnen we beginnen met het maken van onze eigen tests.
Krijg een overzicht
Leren van reeds ontwikkelde tests
In de ontwikkelversie van het Joomla CMS zijn er al Cypress testen aanwezig, deze staan in de folder /tests/System/integration
Wie graag leert door bijvoorbeeld te leren vindt hier een passende inleiding.
Importeer code voor repetitieve taken
De Joomla-ontwikkelaars werken aan het NodeJs- project joomla-cypress , dat testcode biedt voor algemene testgevallen. Deze worden geïmporteerd tijdens de installatie van de ontwikkelaarsversie van het CMS met behulp van npm install
via
package.json
en via de- ondersteuningsbestand
/tests/System/support/index.js
Het ondersteuningsbestand wordt gedefinieerd in de configuratiecypress.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",
...
}
}
Een voorbeeld is het klikken op een knop op de werkbalk.Zorgt er bijvoorbeeld Cypress.Commands.add('clickToolbarButton', clickToolbarButton)
voor dat de opdracht clickToolbarButton()
beschikbaar komt in de aangepaste tests en via cy.clickToolbarButton('new')
een klik op de knop New
wordt gesimuleerd.De code die hiervoor nodig is, is weergegeven in onderstaande codesnipped.
// 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)
...
De volgende code toont een ander voorbeeld, de login op het administratiegebied.
// /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)
...
Veelvoorkomende taken in de individuele omgeving
In de directory /tests/System/support
vindt u veelvoorkomende taken in de individuele omgeving.Om deze eenvoudig te kunnen hergebruiken, worden ze geïmporteerd via het ondersteuningsbestand.Een /tests/System/support/index.js
voorbeeld van een veel voorkomende taak is het inloggen op het beheergedeelte, dat wordt afgehandeld in het bestand /tests/System/support/commands.js
de functie gebruiken doAdministratorLogin
.
De volgende code laat ook zien hoe de informatie uit de cypress.config.js
configuratie wordt gebruikt in de tests. krijgt de waarde van de eigenschap binnen de groep Cypress.env('username')
toegewezen .username
env
Ook kunnen we hier zien hoe je commando's overschrijft. Cypress.Commands.overwrite('doAdministratorLogin' ...),
overschrijft de code die we net in het pakket zagen joomla-cypress
. Het voordeel is dat gebruiker en wachtwoord automatisch worden gebruikt vanuit de individuele configuratie.
// /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 });
});
...
Eigen Joomla Extensie installeren
Om te zien hoe u uw eigen code kunt testen, installeren we een eenvoudig voorbeeldcomponent via de Joomla-backend.Het installatiebestand kunt u downloaden van Codeberg .
Na installatie vindt u een link naar de weergave van de Foo-component in de linkerzijbalk van de Joomla-backend.
We hebben nu een testomgeving opgezet en code om te testen.
De eerste eigen test
Haken
Bij het testen van de backend zul je merken dat je elke test moet starten met een login. Deze overbodige code kunnen we voorkomen door de beforeEach()
functie te gebruiken. Deze zogenaamde hook voert de code uit die we invoeren voordat elke test wordt uitgevoerd. Vandaar de naam beforeEach()
.
Cypress biedt verschillende soorten hooks , waaronder before
en after
hooks die voor of na de tests in een testgroep worden uitgevoerd, en beforeEach
en afterEach
hooks die voor of na elke individuele test in de groep worden uitgevoerd. Hooks kunnen globaal of binnen een specifiek described
blok worden gedefinieerd. codevoorbeeld in het bestand zorgt ervoor dat vóór elke test binnen het blok tests/System/integration/administrator/components/com_foos/FoosList.cy.js
een login wordt uitgevoerd in de backend .described
test com_foos features
We beginnen nu met het praktische gedeelte en maken het bestand tests/System/integration/administrator/components/com_foos/FoosList.cy.js
met de volgende codesnipt voordat we de eerste productieve test schrijven. Ons eerste voorbeeld zou ons met succes moeten aanmelden bij de backend vóór elke test! We zullen dit testen na het maken van de eerste test.
// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
describe('Test com_foos features', () => {
beforeEach(() => {
cy.doAdministratorLogin()
})
})
Opmerking: Hooks, die zijn geïmplementeerd in bestand /tests/System/support/index.js
, zijn van toepassing op elk testbestand in het testpak.
Een geslaagde proef
Het onderdeel dat we hebben geïnstalleerd om te testen, bevat de drie elementen Astrid
, Nina
en Elmar
. Eerst testen we of deze elementen met succes zijn gemaakt.
// 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()
})
})
Opmerking: de functie checkForPhpNoticesOrWarnings()
die u in het bestand /node_modules/joomla-cypress/src/support.js
.
We krijgen het DOM-element main
via het Cypress-commando get
U zou uw zojuist gemaakte test moeten vinden FooList.cy.js
in de lijst met beschikbare tests in de linkerzijbalk. Als dit niet het geval is, sluit u de browser en voert u deze npm run cypress:open
opnieuw uit.
Klik op de naam van de test om deze uit te voeren.Het zou succesvol moeten eindigen en u ziet groene berichten.
Een mislukte proef
Voeg de regel toe cy.get('main').should('contain.text', 'Sami')
aan het testbestand zodat de run mislukt. Er is geen element met deze naam. Na het opslaan van het testbestand merkt Cypress de wijziging op. Na elke wijziging voert Cypress automatisch alle tests in het testbestand opnieuw uit.
// 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()
})
})
Zoals verwacht mislukt de test. Er zijn rode berichten. U kunt de code van elke teststap in de linkerzijbalk zien. Het is dus mogelijk om de reden voor de fout te vinden. Voor elke stap is er een momentopname van het HTML-document, zodat u de opmaak op elk moment kunt controleren. Dit is handig, vooral tijdens de ontwikkeling.
Voer slechts één test uit in een bestand
Onze demo-extensie bevat meer dan één lay-out. Voeg een test toe voor het testen van de lay-out met lege toestand. Aangezien we nu twee tests in dit bestand hebben, zal Cypress altijd beide tests uitvoeren elke keer dat we het bestand opslaan. We kunnen zo gebruiken dat slechts één .only()
test is geëxecuteerd:
// 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.')
})
})
Tijdens de ontwikkeling is dit erg handig.
Speciale testattributen
Nu testen we graag de frontend voor onze component, dit doen we in een apart bestand /tests/System/integration/site/components/com_foos/FooItem.cy.js
.
Meestal gebruiken we een CSS-klasse om elementen in Joomla-tests te krijgen. Hoewel dit volkomen geldig is en zal werken, wordt het eigenlijk niet aanbevolen. Waarom niet? Wanneer u CSS-klassen of ID's gebruikt, bindt u uw tests aan dingen die zullen hoogstwaarschijnlijk in de loop van de tijd veranderen. Klassen en ID's zijn bedoeld voor ontwerp, lay-out en soms via JavaScript voor controle, die gemakkelijk kunnen worden gewijzigd. Als iemand een klassenaam of ID verandert, werken uw tests niet meer. Om uw tests minder broos en meer toekomstbestendig, raadt Cypress aan om speciale gegevensattributen voor uw elementen te maken, specifiek voor testdoeleinden.
Ik zal het attribuut gebruiken data-test
voor de elementen.Eerst voeg ik het attribuut toe data-test="foo-main"
aan de productiecode.
// /components/com_foos/tmpl/foo/default.php
\defined('_JEXEC') or die;
?>
<div data-test="foo-main">
Hello Foos
</div>
Vervolgens test ik de productiecode door te zoeken naar het kenmerk [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()
})
})
Een menu-item testen en enkele gedachten over gebeurtenissen, wachttijden en best practices
Nu wil ik graag het maken van een menu-item voor onze component testen.Ik doe dit in een apart bestand.Deze /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js
code is complex en vertoont veel speciale functies.
Eerst heb ik een constante gedefinieerd waarin ik alle relevante eigenschappen van het menu-item instel, dit heeft als voordeel dat ik bij wijzigingen van een relevante eigenschap maar op één plek hoef aan te passen:
const testMenuItem = {
'title': 'Test MenuItem',
'menuitemtype_title': 'COM_FOOS',
'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
}
Vervolgens zie je de hele code van het bestand 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 deze code ziet u een voorbeeld van iets testen en vervolgens alles verwijderen - en zo de begintoestand herstellen. Op deze manier kunt u de tests zo vaak herhalen als u wilt. Zonder de begintoestand te herstellen zal de tweede testrun mislukken omdat Joomla kan geen twee vergelijkbare elementen opslaan.
Opmerking: een test moet zijn:
- herhaalbaar.
- simpel gehouden Concreet betekent dit dat het een beperkt probleem moet testen en de code hiervoor niet te uitgebreid moet zijn.
- onafhankelijk van andere testen.
- En je kunt zien hoe je een onderschepte route gebruikt die is gedefinieerd met
cy.intercept()
[^docs.cypress.io/api/commands/intercept] als een alias, en vervolgens wacht op de route die is gedefinieerd als een alias metcy.wait()
.
Bij het schrijven van tests voor dergelijke toepassingen komt men in de verleiding om willekeurige waarden te gebruiken, zoals
cy.wait(2000);
in decy.wait
opdracht. Het probleem met deze aanpak is dat hoewel dit goed kan werken in ontwikkeling. Het is echter niet gegarandeerd dat het altijd werkt. Waarom? Omdat het onderliggende systeem is afhankelijk van dingen die moeilijk te voorspellen zijn, daarom is het altijd beter om precies te definiëren waar je op zit te wachten.
- De code laat ook zien hoe te wachten op een waarschuwing en deze te bevestigen .
cy.on("window:confirm", (s) => { return true; });
- Last but not least bevat de testcode Cypress ingebouwde en Joomla-typische functies die bijvoorbeeld door extensie-ontwikkelaars kunnen worden hergebruikt,
cy.setFilter('published', 'Trashed')
ofcy.clickToolbarButton('Save & Close')
zijn functies waarin oplossingen voor individuele tests in het algemeen te vinden zijn en die met name Joomla-ontwikkelaars vaak nodig hebben .Async- en synchronisatiecode mixen
Cypress-commando's zijn asynchroon, dat wil zeggen dat ze geen waarde retourneren, maar
generate
deze.Wanneer we Cypress starten, voert het de commando's niet onmiddellijk uit, maar leest ze serieel en zet ze in de wachtrij.Als u asynchrone en synchrone code in tests combineert, kan onverwachte resultaten opleveren. Als u de volgende code uitvoert, krijgt u tegen de verwachting in een foutmelding. U had toch ook verwacht datmainText = $main.text()
de waarde zou veranderenmainText
. MaarmainText === 'Initial'
is nog steeds geldig aan het einde. Hoe komt dat? Cypress voert eerst de synchrone code uit op aan het begin en aan het einde. Pas dan roept het het asynchrone deel inside aanthen()
. Dat wil zeggen, de variabelemainText
wordt geïnitialiseerd en direct daarna wordt gecontroleerd of het veranderd is - wat natuurlijk niet het geval is.
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}`); }
De verwerking van de wachtrij wordt heel duidelijk en visueel als men de uitvoering van de volgende code in de console van de browser observeert: De tekst 'Cypress Test.' verschijnt lang voordat de inhoud van het element wordt getoond, hoewel de regels
main
code in een andere volgorde.
cy.get('main').then(function(e){ console.log(e.text()) }) console.log('Cypress Test.')
Stubs en spionnen
A
stub
is een manier om het gedrag van de functie te simuleren waarvan de tests afhankelijk zijn. In plaats van de eigenlijke functie aan te roepen, vervangt de stub die functie en retourneert een vooraf gedefinieerd object. Het wordt meestal gebruikt in unit-tests, maar kan ook worden gebruikt voor eindtesten. - tot het einde testen.A
spy
is vergelijkbaar met destub
, maar niet precies hetzelfde. Het verandert het gedrag van een functie niet, maar laat het zoals het is. Het legt wat informatie vast over hoe de functie wordt aangeroepen. Bijvoorbeeld om te controleren of de functie wordt aangeroepen met de juiste parameters, of om te tellen hoe vaak de functie wordt aangeroepen.Het volgende voorbeeld toont a
spy
en astub
in actie. Viaconst stub = cy.stub()
maken we hetstub
element en bepalen in de volgende stap watfalse
wordt geretourneerd voor de eerste aanroep entrue
voor de tweede. Met behulpcy.on('window:confirm', stub)
maken we destub
gebruikt voorwindow:confirm'
. In de volgende stap creëren we metcy.spy(win, 'confirm').as('winConfirmSpy')
hetSpy
element , die de aanroep van observeert'window:confirm'
. Nu testen we dat bij de eerste aanroep het verwijderen van de categorie wordt geweigerd en bij de tweede aanroep wordt bevestigd. Daarbij zorgt de ervoorstub
dat we zeker kunnen verwachten welke retourwaarden zullen zijn'window:confirm'
is ingekapseld. helpt@winConfirmSpy
ervoor te zorgen dat de functie daadwerkelijk is aangeroepen - en hoe vaak deze is aangeroepen.
// 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') ...
Als het gewoon een kwestie is van het instellen van een vaste waarde voor de
'window:confirm'
oproep, zal de volgende code het werk doen.
cy.on("window:confirm", (s) => { return true; });
conclusie
In dit artikel heb je de basistheorie en praktische kenmerken van E2E-testen met Cypress gezien. Ik heb de Joomla-installatie gebruikt om te demonstreren hoe verschillende tests kunnen worden geschreven om ervoor te zorgen dat een Joomla-component op een website werkt zoals verwacht. Ik heb ook laten zien hoe de Cypress kan worden aangepast Test Runner in het bestand cypress.json en het gebruik van aangepaste Cypress-commando's Dit is gedaan aan de hand van eenvoudig te volgen voorbeelden.
Ik hoop dat je genoten hebt van de rondleiding door Cypress met Joomla als voorbeeld en dat je veel kennis en inspiratie voor jezelf hebt kunnen meenemen.