End-to-end testování s Joomla! a Cypress - Moje první kroky a myšlenky

Automatizované testy nejsou speciálním nástrojem pro vývojáře softwaru ve velkých projektech. Zejména u rozšíření jsou automatizované testy pomocí k rychlé identifikaci problémů, pomáhají zajistit bezproblémovou funkčnost rozšíření v novějších verzích Joomly.

Vývojáři Joomla Core chtějí, aby vývojáři softwaru třetích stran testovali jejich rozšíření a nacházeli chyby dříve, než je najde uživatel. To vyžaduje spoustu času a je to nudná práce. Proto se to často nedělá. Zvláště ne, pokud se to dělat musí manuálně lidmi pro každou novou verzi Automatizované testování umožňuje opakovat ruční kroky pro každou verzi, aniž by je člověk prováděl sám. Tímto způsobem jsou chyby nalezeny dříve, než se s nimi uživatel setká při přístupu k živému systému.

Mimochodem, každý, kdo se chce podívat na Cypress, najde tento text jako dobré místo, kde začít. Můžete testovat problémy, aniž byste museli vše sami instalovat a konfigurovat. V repozitáři Github projektu Joomla je vše připraveno.

Intro

„I když je pravda, že kvalitu nelze testovat, je stejně zřejmé, že bez testování není možné vyvinout nic kvalitního.“ – [James A. Whittaker]

Než jsem poprvé narazil na Cypress, nedokázal jsem si představit, že bariéry, které často překážely mému testování, budou skutečně poněkud posunuty stranou. Strávil jsem spoustu času testováním softwaru - a dříve ještě více času řešením problémů který vznikl kvůli nedostatku testování! Nyní jsem přesvědčen, že testy, které jsou:

  • co nejblíže k naprogramování,
  • automaticky,
  • často – ideálně po každé změně programu,

    přinést více, než kolik stojí A co víc: testování může být dokonce zábavné.

Testovací metody se vyplatí naučit!Testovací metody jsou dlouhodobé, protože je lze použít nejen s jakýmkoliv programovacím jazykem, ale lze je aplikovat téměř na jakoukoli lidskou práci, jednou za čas byste měli otestovat téměř vše důležité v životě. Testovací metody jsou nezávislé na konkrétních softwarových nástrojích.Na rozdíl od programovacích technik nebo programovacích jazyků, které jsou často v módě i mimo ni, je znalost, jak nastavit dobré testy, nadčasová.

Kdo by měl číst tento text?

Každý, kdo si myslí, že testování softwaru je ztráta času, by se měl podívat na tento článek. Zejména bych chtěl pozvat, aby si tento článek přečetli ty vývojáře, kteří vždy chtěli psát testy pro svůj software - ale nikdy to nedělali pro různé Cypřiš by mohl být způsob, jak tyto překážky odstranit.

Nějaká teorie

Magický trojúhelník

Magický trojúhelník popisuje vztah mezi náklady, požadovaným časem a dosažitelnou kvalitou. Původně byl tento vztah uznáván a popisován v projektovém řízení. Pravděpodobně jste však o tomto napětí slyšeli i v jiných oblastech. Problematika téměř ve všech provozních procesy ve firmě.

Obecně se například předpokládá, že vyšší náklady mají pozitivní dopad na kvalitu a/nebo datum dokončení – tedy čas.

 

Magic Triangle in Project Management – ​​Pokud se do projektu investuje více peněz, má to pozitivní dopad na kvalitu nebo čas.

Naopak úspora nákladů způsobí snížení kvality a/nebo zpoždění dokončení.

Magický trojúhelník v projektovém managementu – Pokud se do projektu investuje méně peněz, má to negativní dopad na kvalitu nebo čas.

Nyní přichází do hry kouzlo: Překonáváme vztah mezi časem, náklady a kvalitou, protože z dlouhodobého hlediska to lze skutečně překonat.

Souvislost mezi časem, náklady a kvalitou lze dlouhodobě překonat.

Možná jste i vy v praxi zažili, že snížení kvality nevede v dlouhodobém horizontu k úspoře nákladů a technický dluh, který tím vzniká, často vede dokonce ke zvýšení nákladů a času navíc.

Z dlouhodobého hlediska lze korelaci mezi náklady, časem a kvalitou skutečně překonat.

Technický dluh označuje mimořádné úsilí spojené s prováděním změn a vylepšení špatně naprogramovaného softwaru ve srovnání s dobře napsaným softwarem. Martin Fowler rozlišuje následující typy technického dluhu: ty, do kterých člověk vstoupil úmyslně, a ty, do kterých vstoupil neúmyslně Rozlišuje také mezi obezřetnými a lehkomyslnými technickými dluhy.

Technický dluh

Náklady a přínosy

V literatuře najdete zdrcující statistiky o šancích na úspěch softwarových projektů. Málo se změnilo na negativním obrazu, který již zaznamenala studie AW Feyhla v 90. letech. Zde v analýze 162 projektů v 50 organizacích , byla stanovena odchylka nákladů oproti původnímu plánování: 70 % projektů vykazovalo odchylku nákladů alespoň 50 %! Něco není v pořádku! To nemůžete jen tak přijmout, že?

Jedním z řešení by bylo upustit od odhadů nákladů úplně a řídit se argumentací hnutí #NoEstimates . Toto hnutí zastává názor, že odhady nákladů v softwarovém projektu jsou nesmyslné. Softwarový projekt obsahuje podle názoru #NoEstimates vždy produkci něco nového. Nové není srovnatelné s již existujícími zkušenostmi, a proto není předvídatelné.

Čím více zkušeností získávám, tím více docházím k závěru, že extrémní názory nejsou dobré. Řešení je téměř vždy uprostřed. Vyhněte se extrémům i v softwarových projektech a hledejte střed. Nemusím mít 100% jistý plán. Ale také byste neměli naivně zakládat nový projekt. Přestože je řízení softwarových projektů a zejména odhad nákladů důležitým tématem, v tomto textu vás již nebudu nudit. Cílem tohoto článku je ukázat, jak E2E testování lze integrovat do praktického pracovního postupu vývoje softwaru.

Integrujte testování softwaru do svého pracovního postupu

Rozhodli jste se otestovat svůj software. Skvělé! Kdy je na to nejlepší čas? Pojďme se podívat na náklady na opravu chyby v různých fázích projektu. Čím dříve chybu objevíte, tím nižší budou náklady na její opravu. .

Relativní náklady na odstraňování závad v různých fázích projektu

Testování a ladění: Existují slova, která jsou často zmiňována jedním dechem a jejichž významy jsou proto vyrovnány. Při bližším zkoumání však tyto termíny znamenají různé výklady. K těmto slovům patří testování a ladění. Tyto dva termíny mají společné že zjišťují poruchy.Jsou ale i rozdíly ve významu.

  • Testy během vývoje naleznou neznámé poruchy, nalezení poruchy je drahé, lokalizace a odstranění chyby je levné.
  • Debuggery opravují závady, které se najdou po dokončení produktu. Nalezení závady je bezplatné, ale lokalizace a oprava chyby je nákladná.

Závěr: Je nejrozumnější začít s integrací testů co nejdříve. Bohužel je to obtížné implementovat v open source projektu jako je Joomla s většinou dobrovolnými přispěvateli.

Průběžná integrace (CI)
Průběžná integrace testů

Představte si následující scénář. Chystá se vydání nové verze oblíbeného systému pro správu obsahu. Vše, čím vývojáři v týmu přispěli od posledního vydání, je nyní poprvé společně použito. funguje? Budou všechny testy úspěšné - jestli projekt vůbec zahrnuje testy. Nebo bude muset být vydání nové verze znovu odloženo a čekají nás nervy drásající hodiny oprav chyb? Mimochodem, odkládání data vydání také není dobré pro image softwarový produkt! Žádný vývojář nezažívá tento scénář rád. Je mnohem lepší kdykoli vědět, v jakém stavu se softwarový projekt aktuálně nachází? Kód, který nezapadá do stávajícího, by měl být integrován až poté, co byl „vyroben aby se vešly“.Zejména v dobách, kdy je stále běžnější, že je třeba opravit bezpečnostní mezeru, by měl být projekt vždy schopen vytvořit verzi! A zde přichází do hry průběžná integrace.

Při nepřetržité integraci jsou jednotlivé prvky softwaru trvale integrovány. Software je vytvářen a testován v malých cyklech. Tímto způsobem se setkáte s problémy při integraci nebo chybnými testy v rané fázi a ne o dny nebo týdny později. Odstraňování problémů je mnohem snazší, protože chyby jsou objeveny těsně před programováním a obvykle je ovlivněna pouze malá část programu Joomla integruje nový kód pomocí nepřetržité integrace Nový kód je integrován pouze tehdy, když projdou všechny testy.

Díky neustálé integraci nového softwaru je odstraňování problémů mnohem snazší, protože chyby jsou objeveny těsně před programováním a obvykle je ovlivněna pouze malá část programu.

Abyste zajistili, že budete mít během nepřetržité integrace neustále k dispozici testy pro všechny části programu, měli byste vyvinout testem řízený software.

Testem řízený vývoj (TDD)

Testem řízený vývoj je programovací technika, která využívá vývoj v malých krocích. Nejprve napíšete testovací kód. Teprve poté vytvoříte programový kód, který má být testován. Jakákoli změna programu je provedena až poté, co testovací kód pro tuto změnu Vaše testy selžou ihned po vytvoření. Požadovaná funkce ještě není v programu implementována. Teprve poté vytvoříte skutečný programový kód - tedy programový kód, který vyhovuje testu.

TDD testy vám pomohou napsat program správně .

Když poprvé uslyšíte o této technice, možná vám tento koncept nebude vyhovovat. „Člověk“ chce koneckonců vždy nejprve udělat něco produktivního. A psaní testů se na první pohled nezdá produktivní. Vyzkoušejte to. Někdy s novou technikou se spřátelíte až poté, co se s ní seznámíte! V projektech s vysokým pokrytím testů se cítím pohodlněji, když přidávám nové funkce.

Pokud si projdete cvičební část na konci textu, můžete si to vyzkoušet. Nejprve vytvořte test a poté napište kód pro Joomla Core. Poté vše společně odešlete jako PR na Github . Pokud by to udělal každý , Joomla bude mít ideální testovací pokrytí.

Vývoj řízený chováním (BDD)

BDD není další programovací technika nebo testovací technika, ale jakýsi osvědčený postup pro vývoj softwaru. BDD se ideálně používá společně s TDD. Behaviour-Driven-Development v zásadě znamená testování nikoli implementace programového kódu, ale provádění - tj. chování programu Test kontroluje, zda je splněna specifikace, tedy požadavek zákazníka.

Když vyvíjíte software způsobem řízeným chováním, testy vám nejen pomohou správně napsat program, ale také vám pomohou napsat správný program .

Co tím myslím: „Napiš správný program"? Stává se, že uživatelé vidí věci jinak než vývojáři. Příkladem je pracovní postup smazání článku v Joomle. Znovu a znovu se setkávám s uživateli, kteří kliknou na ikonu stavu v do koše a jsou překvapeni. Uživatel obvykle intuitivně předpokládá, že položka je nyní trvale smazána, ale je přepnuta z koše do aktivace. Pro vývojáře je kliknutí na ikonu stavu změnou stavu, přepnutím. ve všech ostatních zobrazeních. Proč by to mělo být v koši jinak? Pro vývojáře je funkce implementována bez chyb. Joomla funguje správně. Ale v mých očích funkce není na tom místě ta správná, protože většina uživatelů by ji popsala / požadovala docela jinak .

V Behavior Driven Development jsou požadavky na software popsány prostřednictvím příkladů nazývaných scénáře nebo uživatelské příběhy.

  • silné zapojení koncového uživatele do procesu vývoje softwaru,
  • dokumentaci všech fází projektu s uživatelskými příběhy/případy v textové podobě - ​​obvykle v popisném jazyce v popisném jazyce Gherkin,
  • automatické testování těchto uživatelských příběhů/případových studií,
  • postupná implementace.Takže kdykoliv je přístupný popis implementovaného softwaru.Pomocí tohoto popisu můžete průběžně zajišťovat správnost již implementovaného programového kódu.

Projekt Joomla zavedl BDD v projektu Google Summer of Code . Doufalo se, že uživatelé bez znalosti programování se budou moci snadněji zapojit pomocí Gherkin ). Tento přístup nebyl důsledně dodržován. V té době Joomla používala Codeception jako S Cypressem je také možné vyvíjet vývoj BDD způsobem BDD.

Plánování

Typy testů
  • Unit testy: Unit test je test, který nezávisle testuje nejmenší programové jednotky.
  • Integrační testy: Integrační test je test, který testuje interakci jednotlivých jednotek.
  • E2E testy nebo akceptační testy: Akceptační test kontroluje, zda program plní úlohu definovanou na začátku.
Strategie

Pokud chcete v Joomle přidat novou funkci a zabezpečit ji testy, můžete postupovat dvěma způsoby.

Top-down a bottom-up jsou dva zásadně odlišné přístupy k pochopení a prezentaci složitých problémů. Top-down jde krok za krokem od abstraktního a obecného ke konkrétnímu a konkrétnímu. Pro ilustraci na příkladu: Systém pro správu obsahu jako Joomla obecně prezentuje webové stránky v prohlížeči, konkrétně však v tomto procesu existuje řada drobných dílčích úkolů, jedním z nich je zobrazení konkrétního textu v nadpisu.

Bottom-up popisuje opačný směr: na tomto místě je vhodné si ještě jednou připomenout, že jedním z prvků behaviorálně řízeného vývoje je vytvoření textového popisu chování softwaru.Tento popis akceptačních kritérií pomáhá vytvářet testy – zejména horní -úrovňové end-to-end testy nebo akceptační testy.

Obvyklý přístup k vytváření testů je dnes zdola. Pokud dáváte přednost vývoji softwaru řízenému chováním, měli byste použít opačnou strategii. Měli byste použít strategii shora dolů. Při strategii shora dolů je nedorozumění identifikováno brzy ve fázi návrhu.

Testovací strategie: Testování shora dolů a testování zdola nahoru

  • Testování shora dolů: Při aplikaci strategie shora dolů se začíná akceptačními testy - tedy tou částí systému, která je nejtěsněji spojena s požadavky uživatele. U softwaru napsaného pro lidské uživatele je to obvykle uživatelské rozhraní. . Důraz je kladen na testování toho, jak uživatel interaguje se systémem. Nevýhodou testování shora dolů je, že je třeba věnovat mnoho času vytváření testovacích duplikátů. Komponenty, které ještě nejsou integrované, musí být nahrazeny zástupnými symboly. Na začátku není skutečný programový kód, chybějící části se proto musí vytvářet uměle a postupně se tato umělá data nahrazují skutečně vypočítanými daty.

  • Testování zdola nahoru: Pokud se budete řídit strategií zdola nahoru, začnete s testy jednotek. Na začátku má vývojář na mysli cílový stav. Tento cíl však nejprve rozloží na jednotlivé komponenty. Problém s Přístup zdola nahoru spočívá v tom, že je obtížné otestovat, jak bude komponenta později využita v reálných situacích. Výhodou testování zdola nahoru je, že softwarové části máme hotové velmi rychle. Tyto části je však třeba používat opatrně. Fungují správně. To je to, co zajišťují testy jednotek. Ale zda je konečný výsledek skutečně takový, jak si zákazník software představuje, není zaručeno.

Testovací pyramida od Mikea Cohna

Kolik testů kterého typu testu by mělo být implementováno? Testovací pyramida Mikea Cohna popisuje koncept využití automatizovaných softwarových testů. Pyramida se skládá ze tří úrovní, strukturovaných podle frekvence použití a relevance. ‍

V ideálním případě je základ testovací pyramidy tvořen mnoha rychlými a snadno udržovatelnými jednotkovými testy, čímž lze rychle odhalit většinu chyb.

Na střední úrovni jsou integrační testy, které poskytují služby pro cílené testování kritických rozhraní, doby provádění integračních testů jsou delší a jejich údržba je také složitější než u unit testů.

Vrchol pyramidy tvoří pomalé E2E testy, které někdy vyžadují velkou údržbu.E2E testy jsou velmi užitečné pro testování aplikace jako kompletního systému.

Požadavky

Jaké vybavení potřebujete pro práci na následující praktické části?

Jaké požadavky máte na aktivní práci na následující praktické části Nemusíte splňovat příliš mnoho požadavků, abyste mohli pracovat na obsahu této příručky Samozřejmě musíte mít počítač Vývojové prostředí s Git, NodeJS a Composer a místní webový server by na něm měly být nainstalovány nebo instalovatelné.

Jaké znalosti byste měli mít vy osobně?

Měli byste znát základní techniky programování, v ideálním případě již máte naprogramovanou malou webovou aplikaci, v každém případě byste měli vědět, kam soubory na vývojovém počítači ukládat a jak je načítat do internetového prohlížeče, nové věci.

Vyzkoušejte to. Integrujte testy do svého dalšího projektu. Možná vám vaše první zkušenost s testem ušetří zdlouhavé ladění nebo trapnou chybu ve skutečném systému. Koneckonců, s bezpečnostní sítí testů můžete vyvíjet software s méně stres.

Nastavení

Nastavení Cypress s Joomla!

Ve vývojářské verzi dostupné na Github je Joomla nakonfigurovaná pro Cypress ready. Existují již testy, které můžete použít jako vodítko. Není tedy nutné vše nastavovat sami, abyste získali první přehled. Tímto způsobem můžete experimentovat s Cypress , seznamte se s jeho výhodami a nevýhodami a sami se rozhodněte, zda chcete testovací nástroj používat.

Kroky k nastavení místního prostředí:

Naklonujte úložiště do kořenového adresáře vašeho místního webového serveru:

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

Přejděte do složky joomla-cms:

$ cd joomla-cms

Podle Joomla Roadmap bude další hlavní verze 5.0 vydána v říjnu 2023. Abych byl aktuální, používám zde tuto vývojovou verzi.

Změna na větev 5.0-dev :

$ git checkout 5.0-dev

Nainstalujte všechny potřebné balíčky skladatelů:

$ composer install

Nainstalujte všechny potřebné balíčky npm:

$ npm install

Další informace a pomoc s nastavením vaší pracovní stanice najdete v článku dokumentace k Joomle „Nastavení vaší pracovní stanice pro vývoj Joomly“ . Pro Cypress jsou informace na cypress.io . Ale to v tuto chvíli není nutné. Joomla nastaví vše Stačí nastavit vaše individuální údaje prostřednictvím konfiguračního souboru joomla-cms/cypress.config.js.

Nastavte si svá individuální data. K tomu můžete použít šablonu joomla-cms/cypress.config.dist.jsjako orientaci. V mém případě tento soubor vypadá takto:

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: Tato e-mailová adresa je chráněna před spamboty. Pro její zobrazení musíte mít povolen Javascript.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Konkrétně jsem přidal adresář tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}do specPattern Array, protože tam chci později uložit test pro moduly. Pak jsem změnil uživatelské jméno a hesla, protože chci instalaci otestovat ručně a lépe si zapamatovat ty, které si sami přiřadili. Používám kontejner Docker jako databáze.Proto jsem změnil databázový server a přístupové údaje.A nakonec jsem musel nastavit kořenovou URL http://localhost/joomla-cmsmé instalace Joomly.

Použijte Cypress

Prostřednictvím webového prohlížeče

Zavolejte npm run cypress:openpřes CLI ve vašem kořenovém adresáři Joomla. Krátce nato se otevře aplikace Cypress. Již dříve jsme vytvořili soubor . joomla-cms/cypress.config.dist.jsŽe je detekován, je patrné ze skutečnosti, že E2E Testing je specifikován jako nakonfigurovaný.

Cypress App se otevře po zavolání 96;npm spustit cypress:open96;.

Zde si můžete vybrat, zda chcete spouštět testy E2E a jaký prohlížeč chcete použít, pro příklad jsem zvolil možnost „Zahájit testování ve Firefoxu“.

Testy E2E v aplikaci Cypress: vyberte prohlížeč, který chcete použít.

Zobrazí se seznam všech dostupných testovacích sad a vy můžete kliknout na tu, kterou chcete spustit.Když vyberete testovací sadu, testy se spustí a průběh testů si můžete prohlédnout v reálném čase v prohlížeči.

Testovací sada Joomla ve Firefoxu prostřednictvím aplikace Cypress.

Zatímco testy běží, můžete na jedné straně vidět spuštěný skript a na pravé straně výsledek v prohlížeči. Nejedná se pouze o snímky obrazovky, ale o skutečné snímky prohlížeče v danou chvíli, takže můžete vidět skutečný HTML kód Jsou také možné snímky obrazovky a dokonce i videa z testů.

Probíhá test instalace Joomly.

Vyzkoušejte.Pokud používáte jako, db_host: 'localhost',můžete otestovat instalaci a tím správně nakonfigurovat Joomlu pro práci v následující části tohoto textu.

Pokud jako já používáte externí zdroj (ne lcoalhost; používám docker kontejner) jako db_host, test pro tento druh instalace ještě není připraven. V takovém případě je otázka bezpečnosti v instalační rutině, která je zatím není v testech uvažováno. V tomto případě nainstalujte Joomlu ručně s informacemi zadanými v souboru joomla-cms/cypress.config.js. Následující testy použijí nastavení z tohoto konfiguračního souboru, například pro přihlášení do administrační oblasti Joomly. Tímto způsobem vývojář testu provede nemusí se starat o zadávání přihlašovacích údajů.Shodující se uživatel a heslo se vždy automaticky použije z konfiguračního souboru.

Bezhlavý

Ve výchozím nastavení cypress runspouští všechny testy bezhlavě/joomla-cms/tests/cypress/output/screenshots Následující příkaz provede všechny již nakódované testy a v případě chyby uloží snímky obrazovky do adresáře Výstupní adresář byl nastaven v cypress.config.jssouboru.

$ npm run cypress:run

Další příkazy CLI

Existují další užitečné příkazy, které nejsou implementovány jako skripty v package.jsonprojektu Joomla, spouštím je přes npx [docs.npmjs.com/commands/npx].

cypřiš ověřit

Příkaz cypress verifyověří, zda je Cypress správně nainstalován a lze jej spustit.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
informace o cypřiši

Výstupem příkazu cypress infojsou informace o Cypressu a aktuálním prostředí.

$ 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
cypřišovou verzi

Příkaz cypress versionvytiskne nainstalovanou binární verzi Cypress, verzi balíčku Cypress, verzi Electronu použitou k vytvoření Cypress a verzi přibaleného uzlu.

$ 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

Dokumentace Cypress poskytuje podrobnější informace.

Psaní prvního vlastního testu

Pokud vše doposud fungovalo, můžeme začít vytvářet vlastní testy.

Získejte přehled

Učení z již vyvinutých testů

Ve vývojové verzi redakčního systému Joomla jsou již Cypress testy, které jsou ve složce Ti, /tests/System/integrationkteří se rádi učí na příkladu, zde najdou vhodný úvod.

Importujte kód pro opakující se úkoly

Vývojáři Joomly pracují na projektu NodeJs joomla-cypress , který poskytuje testovací kód pro běžné testovací případy. Tyto jsou importovány během instalace vývojářské verze CMS npm installpomocí

  • package.jsona prostřednictvím
  • soubor podpory /tests/System/support/index.jsSoubor podpory je definován v konfiguraci 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",
    ...
  }
}

Příkladem je kliknutí na tlačítko na panelu nástrojů. Například Cypress.Commands.add('clickToolbarButton', clickToolbarButton)způsobí, že příkaz clickToolbarButton()bude dostupný ve vlastních testech a cy.clickToolbarButton('new')kliknutím na tlačítko Newse simuluje. Kód, který je k tomu zapotřebí, je uveden v kódu níže.

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

Následující kód ukazuje další příklad, přihlášení do administrační oblasti.

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

...
Běžné úkoly v individuálním prostředí

V adresáři /tests/System/supportnaleznete běžné úlohy v jednotlivých prostředích, aby je bylo možné snadno znovu použít, importují se přes soubor podpory /tests/System/support/index.jsPříkladem často se opakující úlohy je přihlášení do administrační oblasti, které je řešeno v souboru /tests/System/support/commands.jspomocí funkce doAdministratorLogin.

cypress.config.jsNásledující kód také ukazuje, jak se v testech používají informace z konfigurace. Cypress.env('username')je přiřazena hodnota vlastnosti usernamev rámci skupiny env.

Také zde vidíme, jak přepsat příkazy Cypress.Commands.overwrite('doAdministratorLogin' ...),přepíše kód, který jsme právě viděli v balíčku.Výhodou joomla-cypressje, že uživatel a heslo jsou automaticky použity z individuální konfigurace.

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

Nainstalujte si vlastní rozšíření Joomla

Abychom viděli, jak otestovat svůj vlastní kód, nainstalujeme jednoduchou ukázkovou komponentu přes backend Joomla Soubor pro instalaci lze stáhnout z Codeberg .

Nainstalujte si vlastní rozšíření Joomla.

Po instalaci najdete odkaz na pohled na komponentu Foo v levém postranním panelu backendu Joomly.

Pohled na ukázkovou komponentu v backendu Joomly.

Nyní máme nastavené testovací prostředí a kód pro testování.

První vlastní test

Háčky

Při testování backendu si všimnete, že každý test musíte spouštět přihlášením.Tomuto nadbytečnému kódu můžeme zabránit pomocí funkce beforeEach()Tento tzv. hook spustí kód, který zadáme před spuštěním každého testu.Odtud název beforeEach().

Cypress nabízí několik typů háčků , včetně háčků beforea afterháčků, které běží před nebo po testech v testovací skupině, a beforeEachháčků afterEach, které se spouštějí před každým jednotlivým testem ve skupině nebo po něm. Háčky lze definovat globálně nebo v rámci konkrétního describedbloku. Další příklad kódu v souboru způsobí, že se v backendu před každým testem v rámci bloku tests/System/integration/administrator/components/com_foos/FoosList.cy.jsprovede přihlášení .describedtest com_foos features

Nyní začneme praktickou částí a vytvoříme soubor tests/System/integration/administrator/components/com_foos/FoosList.cy.jss dalším kódem odstřiženým před napsáním prvního produktivního testu. Náš první příklad by nás měl úspěšně přihlásit do backendu před jakýmkoli testem! Otestujeme to po vytvoření prvního testu.

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

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

})

Poznámka: Háčky, které jsou implementovány v souboru /tests/System/support/index.js, se vztahují na každý testovací soubor v testovacím obleku.

Úspěšný test

Komponenta, kterou jsme nainstalovali pro testování, obsahuje tři prvky Astrida Nina. ElmarNejprve otestujeme, zda byly tyto prvky úspěšně vytvořeny.

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

Poznámka: Funkce, checkForPhpNoticesOrWarnings()kterou najdete v souboru /node_modules/joomla-cypress/src/support.js.

Prvek DOM získáme mainpřes Cypress příkaz get

Váš právě vytvořený test byste měli najít FooList.cy.jsv seznamu dostupných testů v levém postranním panelu. Pokud tomu tak není, zavřete prohlížeč a spusťte jej npm run cypress:openznovu.

Joomla spustit test pro vlastní rozšíření.

Klikněte na název testu pro jeho spuštění, měl by skončit úspěšně a uvidíte zelené zprávy.

Zobrazení po úspěšném provedení testu.

Neúspěšný test

Přidejte řádek cy.get('main').should('contain.text', 'Sami')do testovacího souboru, aby běh selhal Neexistuje žádný prvek s tímto názvem Po uložení testovacího souboru Cypress zaznamená změnu Po každé změně Cypress automaticky znovu spustí všechny testy v testovacím souboru.

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

Podle očekávání se test nezdařil. Jsou zde červené zprávy. Kód každého testovacího kroku vidíte v levém postranním panelu. Je tedy možné najít důvod chyby. Ke každému kroku je k dispozici snímek HTML dokumentu, takže můžete značky kdykoli zkontrolovat. To je užitečné, zejména během vývoje.

Pohled po testu selhal.

Spusťte pouze jeden test v souboru

Naše demo rozšíření obsahuje více než jedno rozvržení. Přidejte test pro testování rozvržení prázdného stavu. Protože nyní máme v tomto souboru dva testy, Cypress vždy spustí oba testy při každém uložení souboru. Můžeme použít tak, že pouze jeden .only()test se provádí:

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

Během vývoje je to velmi pohodlné.

Speciální testovací atributy

Nyní bychom rádi otestovali frontend pro naši komponentu, a to v samostatném souboru /tests/System/integration/site/components/com_foos/FooItem.cy.js.

Většinu času používáme třídu CSS k získání prvků v testech Joomla. I když je to dokonale platné a bude to fungovat, ve skutečnosti se to nedoporučuje. Proč ne? Když používáte třídy CSS nebo ID, svážete své testy s věcmi, které se s největší pravděpodobností časem změní. Třídy a ID slouží k návrhu, rozvržení a někdy prostřednictvím JavaScriptu pro ovládání, které se může snadno změnit. Pokud někdo změní název třídy nebo ID, vaše testy již nebudou fungovat. Aby vaše testy byly méně křehké a Cypress doporučuje vytvořit speciální datové atributy pro vaše prvky speciálně pro účely testování.

Atribut použiji data-testpro prvky Nejprve přidám atribut data-test="foo-main"do výrobního kódu.

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

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

Poté otestuji produkční kód hledáním atributu [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()
  })
})
Testování položky nabídky a několik myšlenek na události, čekání a osvědčené postupy

Nyní bych rád otestoval vytvoření položky nabídky pro naši komponentu. Dělám to v samostatném souboru /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js. Tento kód je složitý a ukazuje spoustu speciálních funkcí.

Nejprve jsem si definoval konstantu, ve které nastavuji všechny relevantní vlastnosti položky menu, což má tu výhodu, že v případě změny příslušné vlastnosti musím upravovat pouze na jednom místě:

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

Dále uvidíte celý kód souboru 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')
  })
})
  • V tomto kódu můžete vidět příklad testování něčeho a následného smazání všeho - tedy obnovení počátečního stavu. Tímto způsobem můžete testy opakovat, kolikrát chcete. Bez obnovení počátečního stavu se druhé testovací spuštění nezdaří, protože Joomla nelze uložit dva podobné prvky.

Poznámka: Test by měl být:

  • opakovatelný.
  • Konkrétně to znamená, že by měl testovat omezený problém a kód pro to by neměl být příliš rozsáhlý.
  • nezávislé na jiných testech.
  • A můžete vidět, jak použít zachycenou trasu definovanou pomocí cy.intercept()[^docs.cypress.io/api/commands/intercept] jako alias, a pak počkat na trasu definovanou jako alias pomocí cy.wait().

Při psaní testů pro takové aplikace jsme v pokušení používat náhodné hodnoty, například cy.wait(2000);v cy.waitpříkazu. Problém tohoto přístupu je, že i když to může fungovat dobře ve vývoji. Není však zaručeno, že bude fungovat vždy. Proč? Protože základní systém závisí na věcech, které je těžké předvídat, proto je vždy lepší přesně definovat, na co čekáte.

  • Kód také ukazuje, jak čekat na upozornění a potvrdit jej.
cy.on("window:confirm", (s) => {
  return true;
});
  • V neposlední řadě obsahuje testovací kód vestavěný Cypress a funkce typické pro Joomlu, které mohou vývojáři rozšíření znovu použít. Například nebo jsou to funkce, cy.setFilter('published', 'Trashed')ve cy.clickToolbarButton('Save & Close')kterých lze obecně najít řešení pro jednotlivé testy a které zejména vývojáři Joomly často potřebují .
Míchání asynchronního a synchronizačního kódu

Příkazy Cypress jsou asynchronní, to znamená, že nevrací hodnotu, ale generateona Když spustíme Cypress, neprovede příkazy okamžitě, ale čte je sériově a řadí je do fronty Pokud v testech namícháte asynchronní a synchronní kód, může získat neočekávané výsledky. Pokud spustíte následující kód, dostanete proti očekávání chybu. Určitě byste také očekávali, že mainText = $main.text()změní hodnotu . Na konci je mainTextvšak stále platný. Proč? Cypress nejprve spustí synchronní kód na mainText === 'Initial'na začátku a na konci.Teprve pak zavolá asynchronní část inside.To then()znamená,že se proměnná mainTextinicializuje a hned poté se zkontroluje,zda se změnila -což samozřejmě není tento případ.

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

Zpracování fronty se stává zcela jasným a vizuálním, pokud pozorujete provádění následujícího kódu v konzole prohlížeče. Text 'Cypress Test.' se objeví dlouho před zobrazením obsahu prvku, ačkoli řádky kódu mainjsou v jiném pořadí.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Pahýly a špióni

A stubje způsob, jak simulovat chování funkce, na které testy závisí. Namísto volání skutečné funkce stub tuto funkci nahradí a vrátí předdefinovaný objekt. Obvykle se používá v jednotkových testech, ale může být také použit pro ukončení - testování do konce.

A spyje podobné stub, ale není úplně stejné. Nemění chování funkce, ale ponechává ji tak, jak je. Zachycuje některé informace o tom, jak se funkce volá. Například pro kontrolu, zda je funkce volána se správnými parametry nebo spočítat, jak často je funkce volána.

Následující příklad ukazuje a spya a stubv akci. Přes const stub = cy.stub()vytvoříme stubprvek a v dalším kroku určíme, který falsese vrátí pro první volání a pro truedruhé. Pomocí cy.on('window:confirm', stub)uděláme stubbe use for window:confirm'. V dalším kroku vytvoříme s cy.spy(win, 'confirm').as('winConfirmSpy')prvkem Spy, který sleduje volání 'window:confirm'. Nyní testujeme, že při prvním volání je smazání kategorie odmítnuto a při druhém volání je potvrzeno. Tím zajistíme, stubže můžeme s jistotou očekávat, jaké budou návratové hodnoty dodaná. 'window:confirm'je zapouzdřena @winConfirmSpypomáhá zajistit, že funkce byla skutečně volána – a jak často byla volána.

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

Pokud se jedná pouze o nastavení pevné hodnoty pro volání 'window:confirm', následující kód to udělá.

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

závěr

V tomto článku jste viděli základní teorii a praktické rysy E2E testování s Cypress. Použil jsem instalaci Joomla, abych demonstroval, jak psát různé testy, aby bylo zajištěno, že komponenta Joomla na webu funguje podle očekávání. Také jsem ukázal, jak přizpůsobit Cypress Test Runner v souboru cypress.json a jak používat přizpůsobené příkazy Cypress. To bylo provedeno pomocí snadno sledovatelných příkladů.

Doufám, že se vám prohlídka Cypress na příkladu Joomly líbila a že jste si mohli odnést spoustu znalostí a inspirace pro sebe.

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. 

Vezměte prosím na vědomí, že tato webová stránka používá systém automatického překladu, který pomáhá při překladu do různých jazyků. Omlouváme se za jakékoli chyby nebo překlepy, které se mohou v různých textech objevit.