End-to-end test med Joomla! og Cypress - Mine første skridt og tanker

Automatiserede tests er ikke et særligt værktøj for softwareudviklere i store projekter. Især for udvidelser er automatiserede tests en hjælp til hurtigt at identificere problemer. De er med til at sikre, at udvidelser fungerer problemfrit i nyere Joomla-versioner.

Joomla Core-udviklerne vil have tredjeparts softwareudviklere til at teste deres udvidelser, finde fejl, før en bruger finder dem. Dette kræver meget tid og er kedeligt arbejde. Derfor bliver det ofte ikke gjort. Især ikke hvis det skal gøres manuelt af mennesker for hver ny udgivelse. Automatiseret test gør det muligt at gentage de manuelle trin for hver udgivelse, uden at et menneske selv udfører trinene. På denne måde findes fejl, før en bruger støder på dem, når de tilgår live-systemet.

Forresten, vil enhver, der vil tjekke Cypress, finde denne tekst et godt sted at starte. Du kan teste problemer uden at skulle installere og konfigurere alt selv. I Github-lageret i Joomla-projektet er alt klar til at gå.

Intro

"Selvom det er sandt, at kvalitet ikke kan testes, er det lige så indlysende, at uden test er det umuligt at udvikle noget af kvalitet." - [James A. Whittaker]

Inden jeg første gang stødte på Cypress, kunne jeg ikke have forestillet mig, at de barrierer, der ofte kom i vejen for min test, faktisk ville blive flyttet noget til side.Jeg brugte meget tid på at teste software - og tidligere endnu mere tid på at håndtere problemerne der er opstået på grund af manglende test! Nu er jeg overbevist om, at test, der er:

  • så tæt på programmeringen som muligt,
  • automatisk,
  • ofte - helst efter hvert programskift,

    indbring mere, end de koster. Og hvad mere er: test kan endda være sjovt.

Det er værd at lære testmetoder! Testmetoder er langtidsholdbare, fordi de ikke kun kan bruges med et hvilket som helst programmeringssprog, de kan anvendes på næsten ethvert menneskeligt arbejde. Du bør teste næsten alt vigtigt i livet en gang imellem. Testmetoder er uafhængige af specifikke softwareværktøjer.I modsætning til programmeringsteknikker eller programmeringssprog, som ofte er inde og ude af mode, er viden om, hvordan man opsætter gode tests, tidløs.

Hvem skal læse denne tekst?

Enhver, der mener, at softwaretest er spild af tid, bør tage et kig på denne artikel. Jeg vil især gerne invitere de udviklere til at læse denne, som altid har ønsket at skrive test til deres software - men aldrig har gjort for en række forskellige Cypres kunne være en måde at fjerne sådanne barrierer på.

Noget teori

Den magiske trekant

Den Magiske Trekant beskriver forholdet mellem omkostningerne, den nødvendige tid og den opnåelige kvalitet. Oprindeligt blev dette forhold anerkendt og beskrevet i projektledelsen. Men du har sikkert også hørt om denne spænding på andre områder. problem i næsten alle operationelle områder processer i en virksomhed.

For eksempel antages det generelt, at en højere omkostning har en positiv indvirkning på kvalitet og/eller færdiggørelsesdato - det vil sige tid.

 

Den Magiske Trekant i Projektledelse - Hvis der investeres flere penge i projektet, har dette en positiv indflydelse på kvalitet eller tid.

Omvendt vil en omkostningsbesparelse tvinge kvaliteten til at falde og/eller færdiggørelsen forsinkes.

Den magiske trekant i projektledelse - Hvis der investeres færre penge i projektet, har dette en negativ indflydelse på kvalitet eller tid.

Nu kommer magien i spil: Vi overvinder sammenhængen mellem tid, omkostninger og kvalitet!For i det lange løb kan dette faktisk overvindes.

Sammenhængen mellem tid, omkostninger og kvalitet kan overvindes på længere sigt.

Måske har du også i praksis oplevet, at en kvalitetsforringelse ikke giver omkostningsbesparelser på længere sigt.Den tekniske gæld, som dette skaber, fører ofte endda til omkostningsstigninger og ekstra tid.

På længere sigt kan sammenhængen mellem omkostninger, tid og kvalitet faktisk overvindes.

Teknisk gæld refererer til den ekstra indsats, der er forbundet med at lave ændringer og forbedringer af ikke-god programmeret software sammenlignet med velskrevet software.Martin Fowler skelner mellem følgende typer af teknisk gæld: Dem, man har indgået bevidst, og dem, man har indgået utilsigtet. Han skelner også mellem forsigtig og hensynsløs teknisk gæld.

Teknisk gæld

Omkostninger og fordele

I litteraturen finder du ødelæggende statistik om softwareprojekters chancer for succes. Lidt har ændret sig i det negative billede, der allerede blev registreret i en undersøgelse af AW Feyhl i 1990'erne. Her i en analyse af 162 projekter i 50 organisationer , blev omkostningsafvigelsen i forhold til den oprindelige planlægning fastlagt: 70 % af projekterne viste en omkostningsafvigelse på mindst 50 %! Noget er ikke korrekt! Det kan man ikke bare acceptere, vel?

En løsning ville være helt at undvære omkostningsestimater og følge #NoEstimates-bevægelsens argumentation. Denne bevægelse er af den opfattelse, at omkostningsestimater i et softwareprojekt er meningsløse. Et softwareprojekt indeholder ifølge mening fra #NoEstimates altid produktionen af noget nyt Det nye er ikke sammenligneligt med allerede eksisterende erfaringer og dermed ikke forudsigeligt.

Jo mere erfaring jeg får, jo mere kommer jeg til den konklusion, at ekstreme synspunkter ikke er gode. Løsningen er næsten altid i midten. Undgå også ekstremer i softwareprojekter, og se efter en mellemting. Det skal ikke være 100 % sikker plan. Men du skal heller ikke starte et nyt projekt naivt. Selvom softwareprojektstyring og især omkostningsvurdering er et vigtigt emne, vil jeg ikke kede dig med det længere i denne tekst. Fokus i denne artikel er at vise, hvordan E2E test kan integreres i den praktiske arbejdsgang i softwareudvikling.

Integrer softwaretest i din arbejdsgang

Du har besluttet dig for at teste din software. Fantastisk! Hvornår er det bedste tidspunkt at gøre dette? Lad os tage et kig på omkostningerne ved at rette en fejl i de forskellige projektfaser. Jo tidligere du finder en fejl, jo lavere er omkostningerne ved at rette den .

Relative omkostninger til fejlfinding i forskellige projektfaser

Test og debugging: Der er ord, som ofte nævnes i samme åndedrag, og hvis betydning derfor er udlignet. Ved nærmere eftersyn står begreberne dog for forskellige fortolkninger. Test og debugging hører til disse ord. De to begreber har det til fælles at de opdager funktionsfejl, men der er også forskelle i betydningen.

  • Tests finder ukendte fejl under udvikling. Det er dyrt at finde fejlen, mens lokalisering og eliminering af fejlen er billig.
  • Debuggere løser fejl, der findes efter produktet er færdigt. Det er gratis at finde fejlen, men det er dyrt at lokalisere og rette fejlen.

Konklusion: Det giver mest mening at begynde at integrere test så tidligt som muligt.Desværre er dette svært at implementere i et open source-projekt som Joomla med overvejende frivillige bidragydere.

Kontinuerlig integration (CI)
Kontinuerlig integration af tests

Forestil dig følgende scenarie: En ny version af et populært indholdsstyringssystem er ved at blive frigivet. Alt, hvad udviklerne på holdet har bidraget med siden sidste udgivelse, bliver nu brugt sammen for første gang. Spændingen stiger! virker? Vil alle test lykkes - hvis projektet overhovedet integrerer tests. Eller skal udgivelsen af ​​den nye version udskydes igen, og der venter nervepirrende timer med fejlretning forude? Udskydning af udgivelsesdatoen er i øvrigt heller ikke godt for billedet af softwareproduktet! Ingen udviklere kan lide at opleve dette scenarie. Det er meget bedre at vide til enhver tid, i hvilken tilstand softwareprojektet er i øjeblikket? Kode, der ikke passer ind i det eksisterende, bør først integreres efter de er blevet "lavet til at passe".Især i tider, hvor det er mere og mere almindeligt, at et sikkerhedshul skal rettes, bør et projekt altid kunne skabe en udgivelse!Og det er her, kontinuerlig integration spiller ind.

Ved kontinuerlig integration integreres individuelle elementer i softwaren permanent Softwaren skabes og testes i små cyklusser På denne måde støder du på problemer under integration eller fejltest på et tidligt tidspunkt og ikke dage eller uger senere. Fejlfinding er meget nemmere, fordi fejl opdages tæt på programmeringstidspunktet, og normalt er kun en lille del af programmet berørt. Joomla integrerer ny kode ved hjælp af kontinuerlig integration. Ny kode integreres først, når alle test er bestået.

Med en kontinuerlig integration af ny software er fejlfinding meget nemmere, fordi fejlene opdages tæt på programmeringstidspunktet og normalt kun en lille del af programmet er påvirket.

For at sikre, at du til enhver tid har tests for alle programdele tilgængelige under kontinuerlig integration, bør du udvikle testdrevet software.

Testdrevet udvikling (TDD)

Testdrevet udvikling er en programmeringsteknik, der bruger udvikling i små trin. Først skriver du testkoden. Først derefter opretter du den programkode, der skal testes. Enhver ændring af programmet foretages først, efter at testkoden for den ændring har Så dine tests fejler umiddelbart efter oprettelsen Den påkrævede funktion er endnu ikke implementeret i programmet Først derefter opretter du selve programkoden - altså den programkode, der opfylder testen.

TDD-testene hjælper dig med at skrive programmet korrekt .

Når du først hører om denne teknik, er du måske ikke tryg ved konceptet. ""Menneske"" vil jo altid gerne gøre noget produktivt først. Og at skrive test virker ikke produktivt ved første øjekast. Prøv det. Nogle gange du bliver først venner med en ny teknik, når du har lært den at kende! I projekter med høj testdækning føler jeg mig mere tryg, når jeg tilføjer nye funktioner.

Hvis du gennemgår øvelsesdelen i slutningen af ​​teksten, kan du prøve den. Opret først testen og skriv derefter koden til Joomla Core. Send derefter alt sammen som en PR på Github. Hvis alle ville gøre dette , Joomla ville have ideel testdækning.

Adfærdsdrevet udvikling (BDD)

BDD er ikke en anden programmeringsteknik eller testteknik, men en slags best practice til softwareudvikling.BDD bruges ideelt set sammen med TDD.Behaviour-Driven-Development står i princippet for at teste ikke implementeringen af ​​programkoden, men udførelsen - dvs. programmets opførsel En test kontrollerer om specifikationen, altså kundens krav, er opfyldt.

Når du udvikler software på en adfærdsdrevet måde, hjælper test dig ikke kun med at skrive programmet korrekt, men test hjælper dig også med at skrive det rigtige program .

Hvad mener jeg med det: "Skriv det rigtige program"? Det sker, at brugere ser tingene anderledes end udviklere. Arbejdsgangen med at slette en artikel i Joomla er et eksempel. Igen og igen møder jeg brugere, der klikker på statusikonet i papirkurven og bliver overrasket. Brugeren antager normalt intuitivt, at elementet nu er permanent slettet, men det skiftes fra papirkurven til at aktiveres. For udvikleren er et klik på statusikonet en ændring af status, en skift i alle andre visninger. Hvorfor skulle dette være anderledes i skraldespanden? For udvikleren er funktionen implementeret uden fejl. Joomla fungerer korrekt. Men i mine øjne er funktionen ikke den rigtige på det sted, fordi de fleste brugere ville beskrive/bede om det helt anderledes .

I Behavior Driven Development er kravene til softwaren beskrevet gennem eksempler kaldet scenarier eller brugerhistorier Karakteristika for Behavior Driven Development er

  • en stærk involvering af slutbrugeren i udviklingsprocessen af ​​softwaren,
  • dokumentationen af ​​alle projektfaser med brugerhistorier/caseeksempler i tekstform - normalt i beskrivelsessproget i beskrivelsessproget Gherkin,
  • automatisk test af disse brugerhistorier/casestudier,
  • successiv implementering.Dermed kan en beskrivelse af den software, der skal implementeres, til enhver tid tilgås.Ved hjælp af denne beskrivelse kan man løbende sikre sig rigtigheden af ​​den allerede implementerede programkode.

Joomla-projektet har introduceret BDD i et Google Summer of Code-projekt . Det var håbet, at brugere uden programmeringskendskab lettere ville kunne deltage ved at bruge Gherkin ). Tilgangen blev ikke fulgt konsekvent op. På det tidspunkt brugte Joomla Codeception som en testværktøj Med Cypress er BDD-udvikling også muligt at udvikle på BDD-måden.

Planlægning

Testtyper
  • Enhedstest: En enhedstest er en test, der tester de mindste programenheder uafhængigt.
  • Integrationstest: En integrationstest er en test, der tester samspillet mellem individuelle enheder.
  • E2E-tests eller accepttests: En accepttest kontrollerer, om programmet opfylder opgaven defineret i begyndelsen.
Strategier

Hvis du vil tilføje en ny funktion i Joomla og sikre den med test, kan du gå videre på to måder.

Top-down og bottom-up er to fundamentalt forskellige tilgange til at forstå og præsentere komplekse problemstillinger. Top-down går trin for trin fra det abstrakte og generelle til det konkrete og specifikke. For at illustrere dette med et eksempel: Et content management system som Joomla præsenterer generelt hjemmesider i en browser Konkret er der dog en række små delopgaver i denne proces.En af dem er opgaven med at vise en bestemt tekst i en overskrift.

Bottom-up beskriver den modsatte retning: På dette tidspunkt er det værd at huske endnu en gang, at et element i adfærdsdrevet udvikling er skabelsen af ​​en tekstlig beskrivelse af softwarens adfærd.Denne beskrivelse af acceptkriterier er med til at skabe tests - især toppen -niveau end-to-end tests eller accepttests.

Den sædvanlige tilgang til at lave test i dag er fra bunden Hvis du foretrækker adfærdsdrevet softwareudvikling, bør du bruge den modsatte strategi Du bør bruge top-down strategien Med en top-down strategi identificeres en misforståelse tidligt. i designfasen.

Teststrategier: Top-down-test og Bottom-up-test

  • Top-down test: Når man anvender top-down strategien, starter man med accepttestene – altså med den del af systemet, der er tættest knyttet til brugerkravene For software skrevet til menneskelige brugere er dette normalt brugergrænsefladen Fokus er på at teste, hvordan en bruger interagerer med systemet En ulempe ved top-down test er, at der skal bruges meget tid på at lave testduplikater. Komponenter, der endnu ikke er integreret, skal erstattes af pladsholdere. er ingen reel programkode i begyndelsen. Derfor skal manglende dele oprettes kunstigt. Efterhånden erstattes disse kunstige data med virkelig beregnede data.

  • Bottom-up-test: Følger du bottom-up-strategien, starter du med enhedstests. I starten har udvikleren måltilstanden i tankerne. Men så opdeler han først dette mål i individuelle komponenter. Problemet med Bottom-up tilgangen er, at det er svært at teste, hvordan en komponent senere skal bruges i virkelige situationer.Fordelen ved bottom-up test er, at vi er færdige med softwaredele meget hurtigt. Disse dele skal dog bruges med forsigtighed. De fungerer korrekt. Det er det, enhedstestene sikrer, men om slutresultatet virkelig er, hvad kunden forestiller sig, at softwaren er, er ikke sikret.

Testpyramiden af ​​Mike Cohn

Hvor mange tests skal der implementeres af hvilken testtype? Testpyramiden af ​​Mike Cohn beskriver et koncept for anvendelsen af ​​de automatiserede softwaretests. Pyramiden består af tre niveauer, struktureret efter brugshyppighed og relevans. ‍

Ideelt set er bunden af ​​testpyramiden dannet af mange hurtige og nemme at vedligeholde enhedstests. På denne måde kan de fleste fejl opdages hurtigt.

På mellemniveau er integrationstestene. De leverer tjenester til målrettet test af kritiske grænseflader. Udførelsestiderne for integrationstests er længere, og deres vedligeholdelse er også mere kompleks end enhedstests.

Toppen af ​​pyramiden består af langsomme E2E-tests, som nogle gange kræver meget vedligeholdelse.E2E-tests er meget nyttige til at teste applikationen som et komplet system.

Krav

Hvilket udstyr skal du bruge til at arbejde med følgende praktiske del?

Hvilke krav har du for at arbejde aktivt med følgende praktiske del Du skal ikke opfylde ret mange krav for at kunne arbejde med indholdet i denne manual Du skal selvfølgelig have en computer Et udviklingsmiljø med Git, NodeJS og Composer og en lokal webserver skal være installeret eller installeret på den.

Hvilken viden skal du personligt have?

Du bør kende grundlæggende programmeringsteknikker. Ideelt set har du allerede programmeret en lille webapplikation. Under alle omstændigheder bør du vide, hvor du skal gemme filer på din udviklingscomputer, og hvordan du indlæser dem i din internetbrowser. ud af nye ting.

Prøv det. Integrer tests i dit næste projekt. Måske vil din første oplevelse af en test spare dig for en kedelig fejlfindingssession eller en pinlig fejl i det rigtige system. Når alt kommer til alt, med et sikkerhedsnet af test, kan du udvikle software med mindre stress.

Sætte op

Opsætning af Cypress med Joomla!

I den udviklerversion, der er tilgængelig på Github, er Joomla Cypress klar konfigureret. Der findes allerede test, som du kan bruge som guide. Så det er ikke nødvendigt at sætte alt op selv for at få et første overblik. På denne måde kan du eksperimentere med Cypress , lær om dets fordele og ulemper, og beslut selv, om du vil bruge testværktøjet.

Trin til opsætning af det lokale miljø:

Klon lageret til roden af ​​din lokale webserver:

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

Naviger til mappen joomla-cms:

$ cd joomla-cms

Ifølge Joomla Roadmap vil den næste store version 5.0 blive frigivet i oktober 2023. For at være opdateret bruger jeg denne udviklingsversion her.

Skift til branch 5.0-dev :

$ git checkout 5.0-dev

Installer alle nødvendige komponistpakker:

$ composer install

Installer alle nødvendige npm-pakker:

$ npm install

For mere information og hjælp til opsætning af din arbejdsstation, se Joomla-dokumentationsartiklen "Opsætning af din arbejdsstation til Joomla-udvikling" . For Cypress er der information på cypress.io . Men det er ikke nødvendigt på dette tidspunkt. Joomla sætter alt op. Du skal blot opsætte dine individuelle data via konfigurationsfilen joomla-cms/cypress.config.js.

Opsæt dine individuelle data. Til dette kan du bruge skabelonen joomla-cms/cypress.config.dist.jssom orientering. I mit tilfælde ser denne fil sådan ud:

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: Denne e-mail adresse bliver beskyttet mod spambots. Du skal have JavaScript aktiveret for at vise den.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Helt konkret har jeg tilføjet mappen tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}til specPattern Array'et, fordi jeg vil gemme test til moduler der senere. Så har jeg ændret brugernavn og adgangskoder, fordi jeg også vil teste installationen manuelt og huske de selvtildelte bedre. Jeg bruger en Docker container som database Derfor ændrede jeg databaseserveren og adgangsdataene, og til sidst skulle jeg indstille root-URL'en http://localhost/joomla-cmstil min Joomla installation.

Brug Cypres

Via webbrowser

Ring npm run cypress:openvia CLI i din Joomla rodmappe Kort tid efter åbner Cypress app'en Vi har tidligere oprettet filen joomla-cms/cypress.config.dist.jsAt dette registreres kan ses af at E2E Testing er angivet som konfigureret.

Cypress App åbner efter opkald til 96;npm run cypress:open96;.

Her kan du vælge, om du vil køre E2E-testene og hvilken browser, du vil bruge. For eksempel valgte jeg muligheden "Start Testing in Firefox".

E2E-test i Cypress-appen: vælg den browser, du vil bruge.

Alle tilgængelige testsuiter vil blive listet, og du kan klikke på den, du vil køre i. Når du vælger en testsuite, kører testene, og du kan se afviklingen af ​​testene i realtid i browseren.

Joomla test suite i Firefox via Cypress App.

Mens testene kører, kan du se det udførte script på den ene side og resultatet i browseren i højre side. Det er ikke bare skærmbilleder, men rigtige snapshots af browseren på det tidspunkt, så du kan se den faktiske HTML-kode Skærmbilleder og endda videoer af testene er også mulige.

Joomla installationstest i gang.

Prøv det, hvis du bruger som db_host: 'localhost',du kan teste installationen og dermed har konfigureret Joomla korrekt til arbejdet med den følgende del af denne tekst.

Hvis du som jeg bruger en ekstern kilde (ikke lcoalhost; jeg bruger en docker-container) som db_host, er testen for denne form for installation ikke klar endnu. I så fald er der et sikkerhedsspørgsmål i installationsrutinen, som er endnu ikke taget med i testene. I dette tilfælde skal du installere Joomla manuelt med de oplysninger, der er indtastet i filen joomla-cms/cypress.config.js. Følgende test vil bruge indstillingerne fra denne konfigurationsfil, for eksempel til at logge ind på Joomla administrationsområdet. På denne måde gør testudvikleren ikke være ligeglad med at indtaste login-data. Den matchende bruger og adgangskode bruges altid automatisk fra konfigurationsfilen.

Hovedløs

Kører som standard cypress runalle tests hovedløst . Følgende kommando udfører alle allerede kodede test og gemmer skærmbilleder i mappen /joomla-cms/tests/cypress/output/screenshotsi tilfælde af en fejl. Outputmappen blev indstillet i cypress.config.jsfilen.

$ npm run cypress:run

Andre CLI-kommandoer

Der er andre nyttige kommandoer, som ikke er implementeret som scripts i package.jsonJoomla-projektet. Jeg udfører dem via npx [docs.npmjs.com/commands/npx].

cypres bekræfte

Kommandoen cypress verifybekræfter, at Cypress er installeret korrekt og kan køres.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
cypres info

Kommandoen cypress infoudsender information om Cypress og det aktuelle miljø.

$ 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
cypres version

Kommandoen cypress versionudskriver den installerede Cypress binære version, versionen af ​​Cypress-pakken, versionen af ​​Electron, der blev brugt til at oprette Cypress, og den medfølgende nodeversion.

$ 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

Cypress' dokumentation giver mere detaljerede oplysninger.

At skrive den første egen prøve

Hvis alt har fungeret indtil videre, kan vi begynde at lave vores egne tests.

Få overblik

At lære af tests, der allerede er udviklet

I udviklingsversionen af ​​Joomla CMS er der allerede Cypress-tests. Disse er i mappen /tests/System/integration. De, der kan lide at lære ved eksempel, finder en passende introduktion her.

Importer kode til gentagne opgaver

Joomla-udviklerne arbejder på NodeJs- projektet joomla-cypress , som leverer testkode til almindelige testcases, som importeres under installationen af ​​udviklerversionen af ​​CMS'et vha npm install.

  • package.jsonog via
  • supportfil /tests/System/support/index.js. Supportfilen er defineret i konfigurationen 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",
    ...
  }
}

Et eksempel er et klik på en knap på værktøjslinjen. For eksempel Cypress.Commands.add('clickToolbarButton', clickToolbarButton)bevirker, at kommandoen clickToolbarButton()er tilgængelig i de brugerdefinerede tests, og via cy.clickToolbarButton('new')et klik på knappen Newsimuleres. Den nødvendige kode til dette er vist i kodestykket nedenfor.

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

Følgende kode viser et andet eksempel, login til administrationsområdet.

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

...
Fælles opgaver i det enkelte miljø

I mappen /tests/System/supportfinder du almindelige opgaver i det enkelte miljø For at disse nemt kan genbruges, importeres de via supportfilen /tests/System/support/index.jsEt eksempel på en hyppigt gentaget opgave er login til administrationsområdet, som håndteres i filen. /tests/System/support/commands.jsved hjælp af funktionen doAdministratorLogin.

Følgende kode viser også, hvordan informationen fra konfigurationen cypress.config.jsbruges i testene. Cypress.env('username')er tildelt værdien af username​​egenskaben i gruppen env.

Vi kan også se her hvordan man overskriver kommandoer Cypress.Commands.overwrite('doAdministratorLogin' ...),overskriver den kode vi lige har set i pakken joomla-cypressFordelen er at bruger og adgangskode automatisk bruges fra den enkelte konfiguration.

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

Installer din egen Joomla-udvidelse

For at se hvordan du tester din egen kode, installerer vi en simpel eksempelkomponent via Joomla backend Filen til installation kan downloades fra Codeberg .

Installer din egen Joomla-udvidelse.

Efter installationen kan du finde et link til visningen af ​​Foo-komponenten i venstre sidebjælke i Joomla-backend.

Visning af eksempelkomponenten i Joomla-backend.

Vi har nu et testmiljø sat op og kode til at teste.

Den første egen test

Kroge

Når du tester backend, vil du bemærke, at du skal starte hver test med et login. Vi kan forhindre denne overflødige kode ved at bruge funktionen. beforeEach()Denne såkaldte hook eksekverer den kode vi indtaster inden hver test køres. Deraf navnet beforeEach().

Cypress leverer flere typer kroge , herunder kroge, der løber før eller efter testene i en testgruppe, og kroge , der løber før eller efter beforehver enkelt test i gruppen. Kroge kan defineres globalt eller inden for en bestemt blok. Den næste kodeeksempel i filen får et login til at blive udført i backend før hver test i blokken .afterbeforeEachafterEachdescribedtests/System/integration/administrator/components/com_foos/FoosList.cy.jsdescribedtest com_foos features

Vi starter nu med den praktiske del og opretter filen tests/System/integration/administrator/components/com_foos/FoosList.cy.jsmed det næste kodestykke, før vi skriver den første produktive test. Vores første eksempel skulle med succes logge os ind i backend før enhver test! Vi tester dette efter at have oprettet den første test.

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

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

})

Bemærk: Hooks, der er implementeret i filen /tests/System/support/index.js, gælder for hver testfil i testdragten.

En vellykket test

Den komponent, vi installerede til test, indeholder de tre elementer Astrid, Ninaog Elmar. Først tester vi, om disse elementer blev oprettet med succes.

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

Bemærk: Funktionen checkForPhpNoticesOrWarnings()du finder i filen /node_modules/joomla-cypress/src/support.js.

Vi får DOM-elementet mainvia Cypress-kommandoen get

Du bør finde din netop oprettede test FooList.cy.jsi listen over tilgængelige tests i venstre sidebjælke. Hvis dette ikke er tilfældet, skal du lukke browseren og køre npm run cypress:openigen.

Joomla køre test for egen udvidelse.

Klik på navnet på testen for at køre den. Den skulle ende vellykket, og du ser grønne meddelelser.

Visningen efter testen er kørt.

En mislykket prøve

Tilføj linjen cy.get('main').should('contain.text', 'Sami')til testfilen, så kørslen mislykkes. Der er intet element med dette navn. Efter at have gemt testfilen, bemærker Cypress ændringen. Efter hver ændring kører Cypress automatisk alle testene i testfilen.

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

Testen mislykkes som forventet. Der er røde beskeder Du kan se koden for hvert testtrin i venstre sidebjælke. Så det er muligt at finde årsagen til fejlen. For hvert trin er der et snapshot af HTML-dokumentet, så du kan tjekke markeringen til enhver tid. Dette er nyttigt, især under udvikling.

Visningen efter testen er mislykket.

Kør kun én test i en fil

Vores demo-udvidelse indeholder mere end ét layout. Tilføj en test til at teste det tomme tilstandslayout. Da vi nu har to test i denne fil, vil Cypress altid køre begge test hver gang vi gemmer filen. Vi kan bruge, så kun én .only()test udføres:

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

Under udvikling er dette meget praktisk.

Særlige testegenskaber

Nu vil vi gerne teste frontend for vores komponent. Det gør vi i en separat fil /tests/System/integration/site/components/com_foos/FooItem.cy.js.

Det meste af tiden bruger vi en CSS-klasse til at få et element i Joomla-tests. Selvom dette er helt gyldigt og vil fungere, anbefales det faktisk ikke. Hvorfor ikke? Når du bruger CSS-klasser eller ID'er, binder du dine tests til ting, der vil højst sandsynligt ændre sig over tid. Klasser og id'er er til design, layout og nogle gange via JavaScript til kontrol, som nemt kan ændres. Hvis nogen ændrer et klassenavn eller id, vil dine test ikke længere virke. For at gøre dine tests mindre skøre og mere fremtidssikret anbefaler Cypress at oprette specielle dataattributter til dine elementer specifikt til testformål.

Jeg vil bruge data-testattributten til elementerne. Først tilføjer jeg attributten data-test="foo-main"til produktionskoden.

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

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

Så tester jeg produktionskoden ved at søge på attributten [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 af et menupunkt og nogle tanker om begivenheder, ventetider og bedste praksis

Nu kan jeg godt lide at teste oprettelsen af ​​et menupunkt for vores komponent. Jeg gør dette i en separat fil /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js. Denne kode er kompleks og viser en masse specielle funktioner.

Først definerede jeg en konstant, hvor jeg indstiller alle relevante egenskaber for menupunktet. Dette har den fordel, at jeg i tilfælde af ændringer af en relevant egenskab kun skal justere ét sted:

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

Dernæst ser du hele koden for filen 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')
  })
})
  • I denne kode kan du se et eksempel på at teste noget og derefter slette alt - og dermed genskabe den oprindelige tilstand. På denne måde kan du gentage testene så mange gange du vil. Uden at genskabe den oprindelige tilstand, vil den anden testkørsel mislykkes, fordi Joomla kan ikke lagre to ens elementer.

Bemærk: En test skal være:

  • gentagelig.
  • Konkret betyder det, at det skal teste et begrænset problem, og koden til dette bør ikke være for omfattende.
  • uafhængig af andre tests.
  • Og du kan se, hvordan du bruger en opsnappet rute defineret med cy.intercept()[^docs.cypress.io/api/commands/intercept] som et alias, og derefter vente på ruten defineret som et alias med cy.wait().

Når man skriver test til sådanne applikationer, er man fristet til at bruge tilfældige værdier som cy.wait(2000);i cy.waitkommandoen. Problemet med denne tilgang er, at selvom dette kan fungere godt i udviklingen. Det er dog ikke garanteret altid at virke. Hvorfor? Fordi det underliggende system afhænger af ting, der er svære at forudsige, og derfor er det altid bedre at definere præcis, hvad du venter på.

  • Koden viser også, hvordan man venter på en advarsel og bekræfter den.
cy.on("window:confirm", (s) => {
  return true;
});
  • Sidst men ikke mindst indeholder testkoden Cypress build-in og Joomla-typiske funktioner, der kan genbruges af udvidelsesudviklere, eller cy.setFilter('published', 'Trashed')er cy.clickToolbarButton('Save & Close')funktioner, hvori der generelt kan findes løsninger til individuelle tests, og som især Joomla-udviklere ofte har brug for. .
Blanding af Async og Sync Code

Cypress-kommandoer er asynkrone, det vil sige, at de ikke returnerer en værdi, men generateden. Når vi starter Cypress, udfører den ikke kommandoerne med det samme, men læser dem serielt og sætter dem i kø. Hvis du blander asynkron og synkron kode i test, kan du kan få uventede resultater. Hvis du kører følgende kode, vil du mod forventning få en fejl. Du ville helt sikkert også have forventet, at det mainText = $main.text()ændrer værdien mainText. Men mainText === 'Initial'er stadig gyldig til sidst. Hvorfor det? Cypress udfører først den synkrone kode kl. begyndelsen og i slutningen. Først derefter kalder den den asynkrone del inde i then(). Det betyder, at variablen mainTextinitialiseres og umiddelbart derefter kontrolleres, om den har ændret sig - hvilket selvfølgelig ikke er tilfældet.

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}`);
}

Behandlingen af ​​køen bliver ret klar og visuel, hvis man observerer udførelsen af ​​følgende kode i browserens konsol Teksten 'Cypress Test.' vises længe før indholdet af elementet vises, selvom kodelinjerne mainer i en anden rækkefølge.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Stubbe og spioner

A stuber en måde at simulere funktionsmåden for den funktion, som testene afhænger af. I stedet for at kalde den faktiske funktion, erstatter stubben den funktion og returnerer et foruddefineret objekt. Det bruges normalt i enhedstest, men kan også bruges til slut -to-end test.

A spyligner stub, men ikke helt det samme. Det ændrer ikke adfærden for en funktion, men lader den være som den er. Den fanger nogle oplysninger om, hvordan funktionen kaldes. For eksempel for at kontrollere, om funktionen kaldes med de korrekte parametre, eller for at tælle hvor ofte funktionen kaldes.

Følgende eksempel viser a og spya stubi aktion. Via const stub = cy.stub()opretter vi stubelementet og bestemmer i næste trin, hvad der falsereturneres for det første kald og truefor det andet. Ved hjælp af cy.on('window:confirm', stub)gør vi det stubbruges til window:confirm'. I næste trin opretter vi med cy.spy(win, 'confirm').as('winConfirmSpy')elementet Spy, som observerer opkaldet af 'window:confirm'. Nu tester vi, at ved det første opkald afvises sletningen af ​​kategorien, og ved det andet opkald bekræftes det. På den måde sikrer vi, stubat vi med sikkerhed kan forvente, hvilke returværdier der vil være leveret. 'window:confirm'er indkapslet. @winConfirmSpyer med til at sikre, at funktionen rent faktisk blev kaldt - og hvor ofte den blev kaldt.

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

Hvis det blot er et spørgsmål om at indstille en fast værdi for opkaldet 'window:confirm', vil følgende kode klare opgaven.

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

konklusion

I denne artikel har du set grundlæggende teori og praktiske funktioner i E2E-test med Cypress. Jeg brugte Joomla-installationen til at demonstrere, hvordan man skriver forskellige tests for at sikre, at en Joomla-komponent på en hjemmeside fungerer som forventet. Jeg viste også, hvordan man tilpasser Cypress Test Runner i cypress.json-filen, og hvordan man bruger tilpassede Cypress-kommandoer. Dette blev gjort ved hjælp af nemme at følge eksempler.

Jeg håber, du nød turen gennem Cypress ved at bruge Joomla som eksempel, og at du var i stand til at tage en masse viden og inspiration væk til dig selv.

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. 

Bemærk venligst, at denne hjemmeside bruger et automatisk oversættelsessystem til at hjælpe med oversættelsen til de forskellige sprog. Vi beklager enhver fejl eller fejl, der kan vises i de forskellige tekster.