End-to-End-Testen mit Joomla! und Cypress – Meine ersten Schritte und Gedanken

Automatisierte Tests sind kein spezielles Werkzeug für Softwareentwickler in großen Projekten. Gerade bei Erweiterungen sind automatisierte Tests eine Hilfe, um Probleme schnell zu erkennen. Sie tragen dazu bei, dass Erweiterungen in neueren Joomla-Versionen reibungslos funktionieren.

Die Joomla Core-Entwickler möchten, dass Softwareentwickler von Drittanbietern ihre Erweiterungen testen, um Fehler zu finden, bevor ein Benutzer sie findet. Das kostet viel Zeit und ist langweilige Arbeit. Deshalb wird es oft nicht getan. Vor allem nicht, wenn es getan werden muss manuell durch Menschen für jede neue Version. Durch automatisierte Tests ist es möglich, die manuellen Schritte für jede Version zu wiederholen, ohne dass ein Mensch die Schritte selbst ausführt. Auf diese Weise werden Fehler gefunden, bevor ein Benutzer beim Zugriff auf das Live-System auf sie stößt.

Wer Cypress ausprobieren möchte, findet mit diesem Text übrigens einen guten Einstieg. Man kann Probleme testen, ohne alles selbst installieren und konfigurieren zu müssen. Im Github-Repository des Joomla-Projekts ist alles startklar.

Einführung

„Während es wahr ist, dass Qualität nicht getestet werden kann, ist es ebenso offensichtlich, dass es ohne Tests unmöglich ist, etwas von Qualität zu entwickeln.“ – [James A. Whittaker]

Bevor ich zum ersten Mal auf Cypress stieß, hätte ich mir nicht vorstellen können, dass die Hürden, die mir beim Testen oft im Weg standen, tatsächlich etwas beiseite gelegt würden. Ich habe viel Zeit damit verbracht, Software zu testen – und früher noch mehr Zeit damit, mich mit den Problemen auseinanderzusetzen Das ist auf mangelnde Tests zurückzuführen! Jetzt bin ich davon überzeugt, dass Tests:

  • so nah wie möglich an der Programmierung,
  • automatisch,
  • häufig - am besten nach jedem Programmwechsel -

    bringen mehr ein, als sie kosten. Und mehr noch: Testen kann sogar Spaß machen.

Es lohnt sich, Testmethoden zu erlernen! Testmethoden sind langlebig, da sie nicht nur mit jeder Programmiersprache verwendet werden können, sondern auf fast jede menschliche Arbeit angewendet werden können. Man sollte fast alles, was im Leben wichtig ist, ab und zu testen. Testmethoden sind unabhängig von bestimmten Softwaretools. Im Gegensatz zu Programmiertechniken oder Programmiersprachen, die oft in und aus der Mode kommen, ist das Wissen, wie man gute Tests aufsetzt, zeitlos.

Wer sollte diesen Text lesen?

Wer meint, Softwaretests seien Zeitverschwendung, sollte sich diesen Artikel einmal anschauen. Insbesondere möchte ich die Entwickler einladen, dies zu lesen, die schon immer Tests für ihre Software schreiben wollten – es aber noch nie für eine Vielzahl getan haben Cypress könnte eine Möglichkeit sein, solche Barrieren zu beseitigen.

Etwas Theorie

Das magische Dreieck

Das Magische Dreieck beschreibt den Zusammenhang zwischen den Kosten, der benötigten Zeit und der erzielbaren Qualität. Ursprünglich wurde dieser Zusammenhang im Projektmanagement erkannt und beschrieben. Allerdings haben Sie wahrscheinlich auch schon aus anderen Bereichen von diesem Spannungsverhältnis gehört. Problem in fast allen betrieblichen Bereichen Prozesse in einem Unternehmen.

Beispielsweise wird allgemein davon ausgegangen, dass sich höhere Kosten positiv auf die Qualität und/oder den Fertigstellungstermin, also die Zeit, auswirken.

 

Das magische Dreieck im Projektmanagement – ​​Wird mehr Geld in das Projekt investiert, wirkt sich dies positiv auf Qualität oder Zeit aus.

Umgekehrt führt eine Kosteneinsparung zu einer Verschlechterung der Qualität und/oder zu einer Verzögerung der Fertigstellung.

Das magische Dreieck im Projektmanagement – ​​Wenn weniger Geld in das Projekt investiert wird, hat dies negative Auswirkungen auf Qualität oder Zeit.

Jetzt kommt der Zauber ins Spiel: Wir überwinden den Zusammenhang zwischen Zeit, Kosten und Qualität! Denn auf lange Sicht lässt sich dieser tatsächlich überwinden.

Der Zusammenhang zwischen Zeit, Kosten und Qualität kann langfristig überwunden werden.

Vielleicht haben auch Sie in der Praxis die Erfahrung gemacht, dass eine Qualitätsminderung auf lange Sicht nicht zu Kosteneinsparungen führt, sondern dass die dadurch entstehenden technischen Schulden oft sogar zu Kostensteigerungen und zusätzlichem Zeitaufwand führen.

Auf lange Sicht kann der Zusammenhang zwischen Kosten, Zeit und Qualität tatsächlich überwunden werden.

Technische Schulden beziehen sich auf den zusätzlichen Aufwand, der mit der Durchführung von Änderungen und Verbesserungen an nicht gut programmierter Software im Vergleich zu gut geschriebener Software verbunden ist. Martin Fowler unterscheidet die folgenden Arten von technischen Schulden: solche, die man absichtlich eingegangen ist, und solche, die man unabsichtlich eingegangen ist Er unterscheidet auch zwischen umsichtigen und rücksichtslosen technischen Schulden.

Technische Schulden

Kosten und Nutzen

In der Literatur findet man verheerende Statistiken über die Erfolgsaussichten von Softwareprojekten. An dem negativen Bild, das bereits in den 1990er Jahren von AW Feyhl in einer Studie aufgezeichnet wurde, hat sich wenig geändert. Hier in einer Analyse von 162 Projekten in 50 Organisationen , Es wurde die Kostenabweichung gegenüber der ursprünglichen Planung ermittelt: 70 % der Projekte wiesen eine Kostenabweichung von mindestens 50 % auf! Irgendwas stimmt nicht! Das kann man doch nicht einfach hinnehmen, oder?

Eine Lösung wäre, auf Kostenschätzungen gänzlich zu verzichten und der Argumentation der #NoEstimates-Bewegung zu folgen. Diese Bewegung ist der Meinung, dass Kostenschätzungen in einem Softwareprojekt unsinnig seien. Ein Softwareprojekt beinhaltet nach Meinung von #NoEstimates immer die Produktion von etwas Neuem. Das Neue ist nicht mit bereits vorhandenen Erfahrungen vergleichbar und daher nicht vorhersehbar.

Je mehr Erfahrung ich sammle, desto mehr komme ich zu dem Schluss, dass extreme Ansichten nicht gut sind. Die Lösung liegt fast immer in der Mitte. Vermeiden Sie Extreme auch in Softwareprojekten und suchen Sie nach einer Mitte. Es muss keine 100%ige Lösung sein. sicher planen. Man sollte aber auch nicht naiv ein neues Projekt starten. Obwohl Software-Projektmanagement und insbesondere die Kostenschätzung ein wichtiges Thema sind, werde ich Sie in diesem Text nicht länger damit langweilen. Der Schwerpunkt dieses Artikels liegt darauf, zu zeigen, wie E2E funktioniert Testen kann in den praktischen Arbeitsablauf der Softwareentwicklung integriert werden.

Integrieren Sie Softwaretests in Ihren Workflow

Sie haben sich entschieden, Ihre Software zu testen. Großartig! Wann ist der beste Zeitpunkt dafür? Werfen wir einen Blick auf die Kosten für die Behebung eines Fehlers in den verschiedenen Projektphasen. Je früher Sie einen Fehler finden, desto geringer sind die Kosten für die Behebung .

Relative Kosten für die Fehlerbehebung in verschiedenen Projektphasen

Testen und Debuggen: Es gibt Wörter, die oft in einem Atemzug genannt werden und deren Bedeutungen daher gleichgesetzt werden. Bei genauerem Hinsehen stehen die Begriffe jedoch für unterschiedliche Interpretationen. Testen und Debuggen gehören zu diesen Wörtern. Die beiden Begriffe haben etwas gemeinsam dass sie Fehlfunktionen erkennen. Es gibt aber auch Unterschiede in der Bedeutung.

  • Tests finden während der Entwicklung unbekannte Fehlfunktionen. Das Auffinden der Fehlfunktion ist teuer, die Lokalisierung und Beseitigung des Fehlers dagegen kostengünstig.
  • Debugger beheben Fehlfunktionen, die erst nach der Fertigstellung des Produkts festgestellt werden. Das Auffinden der Fehlfunktion ist kostenlos, das Auffinden und Beheben des Fehlers ist jedoch teuer.

Fazit: Es ist am sinnvollsten, so früh wie möglich mit der Integration von Tests zu beginnen. Leider ist dies in einem Open-Source-Projekt wie Joomla mit überwiegend freiwilligen Mitwirkenden schwierig umzusetzen.

Kontinuierliche Integration (CI)
Kontinuierliche Integration von Tests

Stellen Sie sich das folgende Szenario vor. Eine neue Version eines beliebten Content-Management-Systems steht kurz vor der Veröffentlichung. Alles, was die Entwickler im Team seit der letzten Veröffentlichung beigesteuert haben, wird nun zum ersten Mal gemeinsam verwendet. Funktioniert? Werden alle Tests erfolgreich sein - wenn das Projekt überhaupt Tests integriert. Oder muss die Veröffentlichung der neuen Version erneut verschoben werden und es stehen nervenaufreibende Stunden der Fehlerbeseitigung bevor? Eine Verschiebung des Veröffentlichungstermins ist übrigens auch nicht gut für das Image von Das Softwareprodukt! Dieses Szenario erlebt kein Entwickler gerne. Viel besser ist es, zu jedem Zeitpunkt zu wissen, in welchem ​​Zustand sich das Softwareprojekt gerade befindet? Code, der nicht zum bestehenden passt, sollte erst nach der „Herstellung“ integriert werden passen".Gerade in Zeiten, in denen es immer häufiger vorkommt, dass eine Sicherheitslücke geschlossen werden muss, sollte ein Projekt immer in der Lage sein, ein Release zu erstellen! Und hier kommt die kontinuierliche Integration ins Spiel.

Bei der kontinuierlichen Integration werden einzelne Elemente der Software dauerhaft integriert. Die Software wird in kleinen Zyklen erstellt und getestet. So stoßen Sie frühzeitig auf Probleme bei der Integration oder fehlerhafte Tests und nicht erst Tage oder Wochen später. Bei einer erfolgreichen Integration Die Fehlersuche ist viel einfacher, da Fehler zeitnah zum Programmierzeitpunkt entdeckt werden und in der Regel nur ein kleiner Teil des Programms betroffen ist. Joomla integriert neuen Code mittels kontinuierlicher Integration. Neuer Code wird erst integriert, wenn alle Tests bestanden sind.

Bei einer kontinuierlichen Integration neuer Software ist die Fehlerbehebung wesentlich einfacher, da die Fehler zeitnah zum Zeitpunkt der Programmierung entdeckt werden und meist nur ein kleiner Teil des Programms betroffen ist.

Um sicherzustellen, dass Ihnen während der kontinuierlichen Integration jederzeit Tests für alle Programmteile zur Verfügung stehen, sollten Sie testgetriebene Software entwickeln.

Testgetriebene Entwicklung (TDD)

Testgetriebene Entwicklung ist eine Programmiertechnik, bei der die Entwicklung in kleinen Schritten erfolgt. Zuerst schreiben Sie den Testcode. Erst dann erstellen Sie den zu testenden Programmcode. Jede Änderung am Programm wird erst vorgenommen, nachdem der Testcode für diese Änderung vorliegt erstellt wurde. Ihre Tests schlagen also sofort nach der Erstellung fehl. Die benötigte Funktion ist noch nicht im Programm implementiert. Erst dann erstellen Sie den eigentlichen Programmcode, also den Programmcode, der den Test erfüllt.

Die TDD-Tests helfen Ihnen, das Programm korrekt zu schreiben .

Wenn Sie zum ersten Mal von dieser Technik hören, sind Sie mit dem Konzept möglicherweise nicht vertraut. „Der Mensch“ möchte schließlich immer zuerst etwas Produktives tun. Und das Schreiben von Tests scheint auf den ersten Blick nicht produktiv zu sein. Probieren Sie es aus. Manchmal Mit einer neuen Technik wird man erst vertraut, wenn man sie kennengelernt hat! Bei Projekten mit hoher Testabdeckung fühle ich mich wohler, wenn ich neue Funktionen hinzufüge.

Wenn Sie den Übungsteil am Ende des Textes durchgehen, können Sie es ausprobieren. Erstellen Sie zuerst den Test und schreiben Sie dann den Code für Joomla Core. Anschließend reichen Sie alles zusammen als PR auf Github ein. Wenn das jeder tun würde , Joomla hätte eine ideale Testabdeckung.

Verhaltensgesteuerte Entwicklung (BDD)

BDD ist keine weitere Programmiertechnik oder Testtechnik, sondern eine Art Best Practice für die Softwareentwicklung. BDD wird idealerweise zusammen mit TDD eingesetzt. Grundsätzlich steht Behaviour-Driven-Development für das Testen nicht der Implementierung des Programmcodes, sondern der Ausführung - also das Verhalten des Programms. Ein Test überprüft, ob die Spezifikation, also die Anforderung des Kunden, erfüllt ist.

Wenn Sie Software verhaltensgesteuert entwickeln, helfen Ihnen Tests nicht nur dabei, das Programm richtig zu schreiben. Tests helfen Ihnen auch dabei, das richtige Programm zu schreiben .

Was meine ich damit: „Das richtige Programm schreiben“? Es kommt vor, dass Benutzer die Dinge anders sehen als Entwickler. Der Workflow beim Löschen eines Artikels in Joomla ist ein Beispiel. Immer wieder treffe ich Benutzer, die auf das Statussymbol im klicken Papierkorb und sind überrascht. Der Benutzer geht normalerweise intuitiv davon aus, dass das Element jetzt endgültig gelöscht ist, aber es wird von „Papierkorb“ auf „Aktiviert“ umgeschaltet. Für den Entwickler ist das Klicken auf das Statussymbol eine Statusänderung, ein Umschalten. In allen anderen Ansichten. Warum sollte das im Papierkorb anders sein? Für den Entwickler ist die Funktion fehlerfrei implementiert. Joomla funktioniert korrekt. Aber in meinen Augen ist die Funktion an dieser Stelle nicht die richtige, da die meisten Benutzer sie ganz anders beschreiben/fordern würden .

Bei der verhaltensgesteuerten Entwicklung werden die Anforderungen an die Software durch Beispiele beschrieben, die als Szenarien oder User Stories bezeichnet werden. Merkmale der verhaltensgesteuerten Entwicklung sind

  • eine starke Einbindung des Endbenutzers in den Entwicklungsprozess der Software,
  • die Dokumentation aller Projektphasen mit User Stories/Fallbeispielen in Textform – meist in der Beschreibungssprache in der Beschreibungssprache Gherkin,
  • automatisches Testen dieser User Stories/Fallstudien,
  • sukzessive Implementierung. Somit ist jederzeit eine Beschreibung der zu implementierenden Software abrufbar. Mit Hilfe dieser Beschreibung können Sie kontinuierlich die Korrektheit des bereits implementierten Programmcodes sicherstellen.

Das Joomla-Projekt hat BDD in einem Google Summer of Code-Projekt eingeführt . Man hoffte, dass Nutzer ohne Programmierkenntnisse mit Gherkin einfacher teilnehmen könnten . Der Ansatz wurde nicht konsequent weiterverfolgt. Damals nutzte Joomla Codeception als Testtool. Mit Cypress ist die BDD-Entwicklung auch auf BDD-Weise möglich.

Planung

Testtypen
  • Unit-Tests: Ein Unit-Test ist ein Test, der kleinste Programmeinheiten unabhängig voneinander testet.
  • Integrationstests: Ein Integrationstest ist ein Test, der das Zusammenspiel einzelner Einheiten testet.
  • E2E-Tests oder Akzeptanztests: Ein Akzeptanztest prüft, ob das Programm die zu Beginn definierte Aufgabe erfüllt.
Strategien

Wenn Sie eine neue Funktion in Joomla hinzufügen und durch Tests absichern möchten, können Sie auf zwei Arten vorgehen.

Top-Down und Bottom-Up sind zwei grundsätzlich unterschiedliche Herangehensweisen, um komplexe Sachverhalte zu verstehen und darzustellen. Top-Down geht Schritt für Schritt vom Abstrakten und Allgemeinen zum Konkreten und Spezifischen. Um dies an einem Beispiel zu veranschaulichen: Ein Content-Management-System wie Joomla In der Regel werden Webseiten in einem Browser dargestellt. Konkret gibt es dabei jedoch eine Reihe kleiner Teilaufgaben. Eine davon ist die Aufgabe, einen bestimmten Text in einer Überschrift anzuzeigen.

Bottom-up beschreibt die umgekehrte Richtung: An dieser Stelle sei noch einmal daran erinnert, dass ein Element der verhaltensgesteuerten Entwicklung die Erstellung einer textlichen Beschreibung des Verhaltens der Software ist. Diese Beschreibung von Akzeptanzkriterien hilft bei der Erstellung von Tests – insbesondere des Top -Level-End-to-End-Tests oder Abnahmetests.

Der übliche Ansatz zur Erstellung von Tests ist heute von unten. Wenn Sie eine verhaltensgesteuerte Softwareentwicklung bevorzugen, sollten Sie die umgekehrte Strategie verwenden. Sie sollten die Top-Down-Strategie verwenden. Mit einer Top-Down-Strategie wird ein Missverständnis frühzeitig erkannt in der Designphase.

Teststrategien: Top-Down-Testing und Bottom-Up-Testing

  • Top-Down-Testen: Bei der Anwendung der Top-Down-Strategie beginnt man mit den Akzeptanztests – also mit dem Teil des Systems, der am engsten mit den Benutzeranforderungen verknüpft ist. Bei Software, die für menschliche Benutzer geschrieben wurde, ist dies normalerweise die Benutzeroberfläche Der Fokus liegt auf dem Testen, wie ein Benutzer mit dem System interagiert. Ein Nachteil des Top-Down-Testens besteht darin, dass viel Zeit für die Erstellung von Testduplikaten aufgewendet werden muss. Komponenten, die noch nicht integriert sind, müssen durch Platzhalter ersetzt werden. Dort Da es sich zu Beginn nicht um echten Programmcode handelt, müssen fehlende Teile künstlich erzeugt werden. Nach und nach werden diese künstlichen Daten dann durch wirklich berechnete Daten ersetzt.

  • Bottom-up-Testing: Wenn Sie die Bottom-up-Strategie verfolgen, beginnen Sie mit Unit-Tests. Zu Beginn hat der Entwickler den Zielzustand vor Augen. Anschließend zerlegt er dieses Ziel jedoch zunächst in einzelne Komponenten. Das Problem mit dem Der Bottom-up-Ansatz besteht darin, dass es schwierig ist, zu testen, wie eine Komponente später in realen Situationen verwendet wird. Der Vorteil des Bottom-up-Testens besteht darin, dass wir sehr schnell fertige Softwareteile haben. Diese Teile sollten jedoch mit Vorsicht verwendet werden. Sie funktionieren zwar korrekt. Das stellen die Unit-Tests sicher. Ob das Endergebnis aber wirklich das ist, was sich der Kunde unter der Software vorstellt, ist nicht gesichert.

Die Testpyramide von Mike Cohn

Wie viele Tests sollen von welcher Testart durchgeführt werden? Die Testpyramide von Mike Cohn beschreibt ein Konzept für den Einsatz automatisierter Softwaretests. Die Pyramide besteht aus drei Ebenen, gegliedert nach Einsatzhäufigkeit und Relevanz. ‍

Idealerweise bilden viele schnelle und einfach zu wartende Unit-Tests die Basis der Testpyramide. So können die meisten Fehler schnell erkannt werden.

Auf der mittleren Ebene befinden sich die Integrationstests. Sie stellen Dienste für das gezielte Testen kritischer Schnittstellen bereit. Die Ausführungszeiten von Integrationstests sind länger und ihre Wartung ist auch komplexer als die von Unit-Tests.

Die Spitze der Pyramide bilden langsame E2E-Tests, die teilweise viel Wartung erfordern. E2E-Tests sind sehr nützlich, um die Anwendung als Gesamtsystem zu testen.

Anforderungen

Welche Ausrüstung benötigen Sie für die Bearbeitung des folgenden Praxisteils?

Welche Voraussetzungen müssen Sie haben, um den folgenden Praxisteil aktiv zu bearbeiten? Sie müssen nicht sehr viele Voraussetzungen erfüllen, um die Inhalte dieses Handbuchs bearbeiten zu können. Natürlich müssen Sie über einen Computer verfügen. Eine Entwicklungsumgebung mit Git, Darauf sollten NodeJS und Composer sowie ein lokaler Webserver installiert bzw. installierbar sein.

Welche Kenntnisse sollten Sie persönlich mitbringen?

Grundlegende Programmiertechniken sollten Sie beherrschen. Idealerweise haben Sie bereits eine kleine Webanwendung programmiert. Auf jeden Fall sollten Sie wissen, wo Sie Dateien auf Ihrem Entwicklungsrechner ablegen und wie Sie diese in Ihren Internetbrowser laden. Neues entdecken.

Probieren Sie es aus. Integrieren Sie Tests in Ihr nächstes Projekt. Vielleicht ersparen Ihnen Ihre ersten Erfahrungen mit einem Test eine mühsame Debugging-Sitzung oder einen peinlichen Fehler im realen System. Schließlich können Sie mit einem Sicherheitsnetz aus Tests Software mit weniger Kosten entwickeln betonen.

Einrichten

Einrichten von Cypress mit Joomla!

In der auf Github verfügbaren Entwicklerversion ist Joomla Cypress ready konfiguriert. Es gibt bereits Tests, an denen Sie sich orientieren können. Es ist also nicht notwendig, alles selbst einzurichten, um sich einen ersten Überblick zu verschaffen. So können Sie mit Cypress experimentieren Informieren Sie sich über die Vor- und Nachteile und entscheiden Sie selbst, ob Sie das Testtool nutzen möchten.

Schritte zum Einrichten der lokalen Umgebung:

Klonen Sie das Repository in das Stammverzeichnis Ihres lokalen Webservers:

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

Navigieren Sie zum Joomla-cms-Ordner:

$ cd joomla-cms

Laut Joomla Roadmap wird die nächste Hauptversion 5.0 im Oktober 2023 erscheinen. Um auf dem neuesten Stand zu sein, verwende ich hier diese Entwicklungsversion.

Wechseln Sie in den Zweig 5.0-dev :

$ git checkout 5.0-dev

Installieren Sie alle benötigten Composer-Pakete:

$ composer install

Installieren Sie alle benötigten npm-Pakete:

$ npm install

Weitere Informationen und Hilfe zum Einrichten Ihres Arbeitsplatzes finden Sie im Joomla-Dokumentationsartikel „Einrichten Ihres Arbeitsplatzes für die Joomla-Entwicklung“ . Für Cypress gibt es Informationen unter cypress.io . Das ist an dieser Stelle aber nicht notwendig. Joomla richtet alles ein Für Sie. Sie müssen lediglich Ihre individuellen Daten über die Konfigurationsdatei einrichten joomla-cms/cypress.config.js.

Richten Sie Ihre individuellen Daten ein. Hierzu können Sie sich an der Vorlage joomla-cms/cypress.config.dist.jsorientieren. In meinem Fall sieht diese Datei so aus:

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: Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

Konkret habe ich das Verzeichnis tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}zum specPattern Array hinzugefügt, weil ich dort später Tests für Module speichern möchte. Dann habe ich den Benutzernamen und die Passwörter geändert, weil ich die Installation auch manuell testen und mir die selbst zugewiesenen besser merken möchte. Ich verwende einen Docker-Container Als Datenbank. Daher habe ich den Datenbankserver und die Zugangsdaten geändert. Und schließlich musste ich die Root-URL http://localhost/joomla-cmsmeiner Joomla-Installation festlegen.

Verwenden Sie Zypresse

Über einen Webbrowser

Aufruf npm run cypress:openper CLI in Ihrem Joomla-Root-Verzeichnis. Kurze Zeit später öffnet sich die Cypress-App. Wir haben zuvor die Datei erstellt. joomla-cms/cypress.config.dist.jsDass dies erkannt wird, erkennen Sie daran, dass E2E Testing als konfiguriert angegeben ist.

Die Cypress-App wird nach dem Aufruf von 96;npm run cypress:open96; geöffnet.

Hier können Sie auswählen, ob Sie die E2E-Tests ausführen möchten und welchen Browser Sie verwenden möchten. Für das Beispiel habe ich die Option „Test in Firefox starten“ gewählt.

E2E-Tests in der Cypress-App: Wählen Sie den zu verwendenden Browser aus.

Alle verfügbaren Testsuiten werden aufgelistet und Sie können auf die Testsuite klicken, die Sie ausführen möchten. Wenn Sie eine Testsuite auswählen, werden die Tests ausgeführt und Sie können die Ausführung der Tests in Echtzeit im Browser verfolgen.

Joomla-Testsuite in Firefox über die Cypress App.

Während die Tests laufen, sieht man auf der einen Seite das ausgeführte Skript und auf der rechten Seite das Ergebnis im Browser. Dabei handelt es sich nicht nur um Screenshots, sondern um echte Schnappschüsse des Browsers in diesem Moment, sodass man den eigentlichen HTML-Code sehen kann Auch Screenshots und sogar Videos der Tests sind möglich.

Joomla-Installationstest läuft.

Probieren Sie es aus. Wenn Sie as verwenden, db_host: 'localhost',können Sie die Installation testen und haben somit Joomla für die Arbeit am folgenden Teil dieses Textes richtig konfiguriert.

Wenn Sie, wie ich, eine externe Quelle (nicht lcoalhost; ich verwende einen Docker-Container) als verwenden db_host, ist der Test für diese Art der Installation noch nicht fertig. In diesem Fall gibt es eine Frage der Sicherheit in der Installationsroutine, was der Fall ist noch nicht in den Tests berücksichtigt. In diesem Fall installieren Sie Joomla manuell mit den in der Datei eingegebenen Informationen joomla-cms/cypress.config.js. Die folgenden Tests verwenden die Einstellungen aus dieser Konfigurationsdatei, beispielsweise für die Anmeldung im Joomla-Administrationsbereich. Auf diese Weise tut der Testentwickler Sie müssen sich nicht um die Eingabe der Anmeldedaten kümmern, der passende Benutzer und das passende Passwort werden immer automatisch aus der Konfigurationsdatei verwendet.

Kopflos

Standardmäßig cypress runwerden alle Tests kopflos/joomla-cms/tests/cypress/output/screenshots ausgeführt. Der folgende Befehl führt alle bereits codierten Tests aus und speichert im Fehlerfall Screenshots im Verzeichnis . Das Ausgabeverzeichnis wurde in der cypress.config.jsDatei festgelegt.

$ npm run cypress:run

Andere CLI-Befehle

Es gibt weitere hilfreiche Befehle, die nicht als Skripte im package.jsonJoomla-Projekt implementiert sind. Ich führe sie über npx aus [docs.npmjs.com/commands/npx].

Zypresse überprüfen

Der cypress verifyBefehl überprüft, ob Cypress korrekt installiert ist und ausgeführt werden kann.

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
Informationen zu Zypressen

Der cypress infoBefehl gibt Informationen über Cypress und die aktuelle Umgebung aus.

$ 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
Zypressenversion

Der cypress versionBefehl gibt die installierte Cypress-Binärversion, die Version des Cypress-Pakets, die zum Erstellen von Cypress verwendete Electron-Version und die gebündelte Knotenversion aus.

$ 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

Die Dokumentation von Cypress bietet detailliertere Informationen.

Den ersten eigenen Test schreiben

Wenn bisher alles funktioniert hat, können wir mit der Erstellung eigener Tests beginnen.

Verschaffen Sie sich einen Überblick

Lernen aus bereits entwickelten Tests

In der Entwicklungsversion des Joomla CMS gibt es bereits Cypress-Tests. Diese befinden sich im Ordner /tests/System/integration. Wer gerne anhand von Beispielen lernt, findet hier einen passenden Einstieg.

Importieren Sie Code für sich wiederholende Aufgaben

Die Joomla-Entwickler arbeiten am NodeJs- Projekt joomla-cypress , das Testcode für gängige Testfälle bereitstellt. Diese werden bei der Installation der Entwicklerversion des CMS npm installüber via importiert

  • package.jsonund über die
  • Support-Datei /tests/System/support/index.js. Die Support-Datei wird in der Konfiguration definiert 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",
    ...
  }
}

Ein Beispiel ist der Klick auf eine Schaltfläche in der Symbolleiste. Beispielsweise Cypress.Commands.add('clickToolbarButton', clickToolbarButton)wird bewirkt, dass der Befehl clickToolbarButton()in den benutzerdefinierten Tests verfügbar ist und über cy.clickToolbarButton('new')einen Klick auf die Schaltfläche Newsimuliert wird. Der dafür erforderliche Code ist im folgenden Codeausschnitt dargestellt.

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

Der folgende Code zeigt ein weiteres Beispiel, den Login zum Administrationsbereich.

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

...
Gemeinsame Aufgaben im individuellen Umfeld

Im Verzeichnis /tests/System/supportfinden Sie häufige Aufgaben in der individuellen Umgebung. Damit diese problemlos wiederverwendet werden können, werden sie über die Support-Datei importiert. Ein /tests/System/support/index.jsBeispiel für eine häufig wiederkehrende Aufgabe ist die Anmeldung am Administrationsbereich, die in der Datei abgewickelt wird /tests/System/support/commands.jsmit der Funktion doAdministratorLogin.

Der folgende Code zeigt außerdem, wie die Informationen aus der cypress.config.jsKonfiguration in den Tests verwendet werden. Cypress.env('username')wird der Wert der usernameEigenschaft innerhalb der Gruppe zugewiesen env.

Außerdem können wir hier sehen, wie man Befehle überschreibt. Cypress.Commands.overwrite('doAdministratorLogin' ...),Überschreibt den Code, den wir gerade im Paket gesehen haben joomla-cypress. Der Vorteil ist, dass Benutzer und Passwort automatisch aus der individuellen Konfiguration verwendet werden.

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

Installieren Sie Ihre eigene Joomla-Erweiterung

Um zu sehen, wie Sie Ihren eigenen Code testen können, installieren wir eine einfache Beispielkomponente über das Joomla-Backend. Die Datei zur Installation kann bei Codeberg heruntergeladen werden .

Installieren Sie eine eigene Joomla-Erweiterung.

Nach der Installation finden Sie in der linken Seitenleiste des Joomla-Backends einen Link zur Ansicht der Foo-Komponente.

Ansicht der Beispielkomponente im Joomla-Backend.

Wir haben jetzt eine Testumgebung und Code zum Testen eingerichtet.

Der erste eigene Test

Haken

Beim Testen des Backends werden Sie feststellen, dass Sie jeden Test mit einem Login starten müssen. Diesen redundanten Code können wir mit der beforeEach()Funktion verhindern. Dieser sogenannte Hook führt den Code aus, den wir vor jedem Testdurchlauf eingeben. Daher der Name beforeEach().

Cypress bietet mehrere Arten von Hooks , darunter beforeHooks after, die vor oder nach den Tests in einer Testgruppe ausgeführt werden, und beforeEachHooks afterEach, die vor oder nach jedem einzelnen Test in der Gruppe ausgeführt werden. Hooks können global oder innerhalb eines bestimmten describedBlocks definiert werden. Der nächste Codebeispiel in der Datei bewirkt, dass vor jedem Test innerhalb des Blocks tests/System/integration/administrator/components/com_foos/FoosList.cy.jseine Anmeldung im Backend durchgeführt wird .describedtest com_foos features

Wir beginnen nun mit dem praktischen Teil und erstellen die Datei tests/System/integration/administrator/components/com_foos/FoosList.cy.jsmit dem nächsten Codesnippt, bevor wir den ersten Produktivtest schreiben. Unser erstes Beispiel sollte uns vor jedem Test erfolgreich im Backend anmelden! Wir werden dies nach der Erstellung des ersten Tests testen.

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

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

})

Hinweis: Hooks, die in file implementiert sind /tests/System/support/index.js, gelten für jede Testdatei im Testanzug.

Ein gelungener Test

Die von uns zum Testen installierte Komponente enthält die drei Elemente Astrid, Ninaund Elmar. Zunächst testen wir, ob diese Elemente erfolgreich erstellt wurden.

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

Hinweis: Die Funktion checkForPhpNoticesOrWarnings()finden Sie in der Datei /node_modules/joomla-cypress/src/support.js.

Das DOM-Element erhalten wir mainüber den Cypress-Befehl get

Sie sollten Ihren soeben erstellten Test FooList.cy.jsin der Liste der verfügbaren Tests in der linken Seitenleiste finden. Ist dies nicht der Fall, schließen Sie bitte den Browser und führen Sie ihn npm run cypress:openerneut aus.

Joomla führt Test für eigene Erweiterung durch.

Klicken Sie auf den Namen des Tests, um ihn auszuführen. Er sollte erfolgreich enden und Sie sehen grüne Meldungen.

Die Ansicht nach erfolgreicher Testdurchführung.

Ein fehlgeschlagener Test

Fügen Sie die Zeile cy.get('main').should('contain.text', 'Sami')zur Testdatei hinzu, damit der Lauf fehlschlägt. Es gibt kein Element mit diesem Namen. Nach dem Speichern der Testdatei bemerkt Cypress die Änderung. Nach jeder Änderung führt Cypress automatisch alle Tests in der Testdatei erneut aus.

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

Wie erwartet schlägt der Test fehl. Es werden rote Meldungen angezeigt. Sie können den Code jedes Testschritts in der linken Seitenleiste sehen. So ist es möglich, die Ursache für den Fehler zu finden. Für jeden Schritt gibt es einen Snapshot des HTML-Dokuments. So können Sie das Markup jederzeit überprüfen. Dies ist besonders während der Entwicklung hilfreich.

Die Ansicht, nachdem der Test fehlgeschlagen ist.

Führen Sie nur einen Test in einer Datei aus

Unsere Demo-Erweiterung enthält mehr als ein Layout. Fügen Sie einen Test zum Testen des leeren Statuslayouts hinzu. Da wir jetzt zwei Tests in dieser Datei haben, führt Cypress bei jedem Speichern der Datei immer beide Tests aus. Wir können also nur einen Test .only()verwenden wird ausgeführt:

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

Während der Entwicklung ist dies sehr praktisch.

Spezielle Testattribute

Nun testen wir gerne das Frontend für unsere Komponente. Dies machen wir in einer separaten Datei /tests/System/integration/site/components/com_foos/FooItem.cy.js.

Meistens verwenden wir eine CSS-Klasse, um Elemente in Joomla-Tests abzurufen. Dies ist zwar vollkommen gültig und funktioniert, wird jedoch nicht wirklich empfohlen. Warum nicht? Wenn Sie CSS-Klassen oder IDs verwenden, binden Sie Ihre Tests an Dinge, die wird sich höchstwahrscheinlich im Laufe der Zeit ändern. Klassen und IDs dienen dem Design, dem Layout und manchmal über JavaScript zur Steuerung, die sich leicht ändern können. Wenn jemand einen Klassennamen oder eine Klassen-ID ändert, funktionieren Ihre Tests nicht mehr. Um Ihre Tests weniger spröde zu machen und Cypress ist zukunftssicherer und empfiehlt die Erstellung spezieller Datenattribute für Ihre Elemente speziell zu Testzwecken.

Ich werde das data-testAttribut für die Elemente verwenden. Zuerst füge ich das Attribut data-test="foo-main"dem Produktionscode hinzu.

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

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

Dann teste ich den Produktionscode, indem ich nach dem Attribut suche [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()
  })
})
Testen eines Menüpunkts und einige Gedanken zu Ereignissen, Wartezeiten und Best Practices

Nun teste ich gerne die Erstellung eines Menüpunktes für unsere Komponente. Dies mache ich in einer separaten Datei /tests/System/integration/administrator/components/com_foos/MenuItem.cy.js. Dieser Code ist komplex und weist viele Besonderheiten auf.

Zuerst habe ich eine Konstante definiert, in der ich alle relevanten Eigenschaften des Menüpunkts einstelle. Das hat den Vorteil, dass ich bei Änderungen einer relevanten Eigenschaft nur an einer Stelle anpassen muss:

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

Als nächstes sehen Sie den gesamten Code der Datei 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 diesem Code sehen Sie ein Beispiel dafür, wie man etwas testet und dann alles löscht – und so den Ausgangszustand wiederherstellt. Auf diese Weise können Sie die Tests beliebig oft wiederholen. Ohne Wiederherstellung des Ausgangszustands schlägt der zweite Testlauf fehl, weil Joomla kann nicht zwei ähnliche Elemente speichern.

Hinweis: Ein Test sollte sein:

  • wiederholbar.
  • einfach gehalten. Konkret bedeutet das, dass es ein begrenztes Problem testen soll und der Code dafür nicht zu umfangreich sein sollte.
  • unabhängig von anderen Tests.
  • Und Sie können sehen, wie Sie eine mit cy.intercept()[^docs.cypress.io/api/commands/intercept] definierte abgefangene Route als Alias ​​verwenden und dann auf die mit als Alias ​​definierte Route warten cy.wait().

Wenn man Tests für solche Anwendungen schreibt, ist man versucht, zufällige Werte wie cy.wait(2000);im cy.waitBefehl zu verwenden. Das Problem bei diesem Ansatz besteht darin, dass dies zwar in der Entwicklung gut funktionieren kann. Es ist jedoch nicht garantiert, dass es immer funktioniert. Warum? Weil Das zugrunde liegende System hängt von Dingen ab, die schwer vorherzusagen sind. Daher ist es immer besser, genau zu definieren, worauf Sie warten.

  • Der Code zeigt auch, wie man auf eine Warnung wartet und diese bestätigt .
cy.on("window:confirm", (s) => {
  return true;
});
  • Nicht zuletzt enthält der Testcode Cypress-Build-Ins und Joomla-typische Funktionen, die von Erweiterungsentwicklern wiederverwendet werden können. Beispielsweise handelt es sich bei cy.setFilter('published', 'Trashed')oder cy.clickToolbarButton('Save & Close')um Funktionen, in denen Lösungen für einzelne Tests im Allgemeinen zu finden sind und die Joomla-Entwickler im Besonderen häufig benötigen .
Mischen von Async- und Sync-Code

Cypress-Befehle sind asynchron, das heißt, sie geben keinen Wert zurück, sondern generateihn. Wenn wir Cypress starten, führt es die Befehle nicht sofort aus, sondern liest sie seriell und stellt sie in die Warteschlange. Wenn Sie in Tests asynchronen und synchronen Code mischen, werden Sie kann zu unerwarteten Ergebnissen führen. Wenn Sie den folgenden Code ausführen, erhalten Sie wider Erwarten einen Fehler. Sicher hätten Sie auch damit gerechnet, dass sich der Wert ändert. mainText = $main.text()Ist mainTextaber mainText === 'Initial'am Ende immer noch gültig. Warum ist das so? Cypress führt den synchronen Code zuerst aus Am Anfang und am Ende. Erst dann ruft es den asynchronen Teil im Inneren auf then(). Das heißt, die Variable mainTextwird initialisiert und gleich danach überprüft, ob sie sich geändert hat – was natürlich nicht der Fall ist.

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

Die Abarbeitung der Warteschlange wird recht anschaulich und anschaulich, wenn man die Ausführung des folgenden Codes in der Konsole des Browsers beobachtet. Der Text „Cypress Test.“ erscheint lange bevor der Inhalt des Elements angezeigt wird, die Codezeilen hingegen mainschon in einer anderen Reihenfolge.

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
Stubs und Spione

A stubist eine Möglichkeit, das Verhalten der Funktion zu simulieren, von der die Tests abhängen. Anstatt die eigentliche Funktion aufzurufen, ersetzt der Stub diese Funktion und gibt ein vordefiniertes Objekt zurück. Es wird normalerweise in Unit-Tests verwendet, kann aber auch für End verwendet werden -Endprüfung.

A spyähnelt dem stub, ist aber nicht genau dasselbe. Es ändert das Verhalten einer Funktion nicht, sondern lässt es unverändert. Es erfasst einige Informationen darüber, wie die Funktion aufgerufen wird. Beispielsweise um zu überprüfen, ob die Funktion aufgerufen wird mit den richtigen Parametern oder um zu zählen, wie oft die Funktion aufgerufen wird.

Das folgende Beispiel zeigt a spyund a stubin Aktion. Über const stub = cy.stub()erstellen wir das stubElement und ermitteln im nächsten Schritt, was falsefür den ersten Aufruf und truefür den zweiten zurückgegeben wird. Mit cy.on('window:confirm', stub)stellen wir die stubVerwendung für her window:confirm'. Im nächsten Schritt erstellen wir mit cy.spy(win, 'confirm').as('winConfirmSpy')dem SpyElement , der den Aufruf von beobachtet 'window:confirm'. Nun testen wir, dass beim ersten Aufruf das Löschen der Kategorie abgelehnt und beim zweiten Aufruf bestätigt wird. Damit stellt der sicher, stubdass wir mit Sicherheit erwarten können, welche Rückgabewerte sein werden geliefert. 'window:confirm'wird gekapselt. @winConfirmSpyhilft sicherzustellen, dass die Funktion tatsächlich aufgerufen wurde – und wie oft sie aufgerufen wurde.

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

Wenn es nur darum geht, einen festen Wert für den 'window:confirm'Aufruf festzulegen, erledigt der folgende Code die Aufgabe.

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

Abschluss

In diesem Artikel haben Sie grundlegende theoretische und praktische Funktionen des E2E-Testens mit Cypress kennengelernt. Ich habe anhand der Joomla-Installation gezeigt, wie man verschiedene Tests schreibt, um sicherzustellen, dass eine Joomla-Komponente auf einer Website wie erwartet funktioniert. Außerdem habe ich gezeigt, wie man Cypress anpasst Test Runner in der cypress.json-Datei und wie man benutzerdefinierte Cypress-Befehle verwendet. Dies wurde anhand leicht verständlicher Beispiele durchgeführt.

Ich hoffe, Ihnen hat der Rundgang durch Cypress am Beispiel von Joomla gefallen und Sie konnten viel Wissen und Inspiration für sich mitnehmen.

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. 

Bitte beachten Sie, dass diese Website ein automatisches Übersetzungssystem verwendet, um die Übersetzung für die verschiedenen Sprachen zu erleichtern. Wir entschuldigen uns für etwaige Fehler oder Tippfehler, die in den verschiedenen Texten auftreten können.