Willkommen bei dieser Serie zur Entwicklung von Laravel-Anwendungen mit einem BDD-Ansatz (BDD = Behavior Driven Development). Full Stack BDD kann kompliziert und einschüchternd wirken. Es gibt genauso viele Möglichkeiten, wie es Entwickler gibt.
In dieser Serie werde ich Sie durch meine Herangehensweise führen, wie Sie mit Behat und PhpSpec eine Laravel-Anwendung von Grund auf entwerfen.
Im Allgemeinen gibt es viele Ressourcen zu BDD, aber Laravel-spezifisches Material ist schwer zu finden. Daher konzentrieren wir uns in dieser Serie mehr auf die Aspekte von Laravel und weniger auf die allgemeinen Dinge, die Sie an vielen anderen Orten nachlesen können.
Bei der Beschreibung von Verhalten, das auch als Schreiben von Geschichten und Spezifikationen bezeichnet wird, verwenden wir einen Outside-In-Ansatz. Dies bedeutet, dass wir jedes Mal, wenn wir eine neue Funktion erstellen, die gesamte User Story schreiben. Dies ist normalerweise aus der Sicht der Kunden oder Stakeholder.
Was erwarten wir, wenn wir das tun??
Es ist uns nicht gestattet, Code zu schreiben, bis ein fehlerhafter, roter Schritt vorliegt, es sei denn, wir konvertieren vorhandenen Code.
Manchmal ist es notwendig, einen kleinen Teil einer Geschichte oder eines Features (zwei Wörter, die ich austauschbar verwende), an dem wir arbeiten, iterativ zu lösen. Dies bedeutet häufig das Schreiben von Spezifikationen mit PhpSpec. Manchmal dauert es viele Iterationen auf Integrations- oder Einheitenebene, bis die gesamte Geschichte (auf Akzeptanzniveau) vorüber ist. Das hört sich alles sehr kompliziert an, ist es aber wirklich nicht. Ich glaube fest an das Learning by Doing, also denke ich, dass alles sinnvoller wird, wenn wir tatsächlich Code schreiben.
Wir werden Geschichten und Spezifikationen auf vier verschiedenen Ebenen schreiben:
Meistens dient unsere Funktionssuite als Akzeptanzschicht. Die Art und Weise, in der wir unsere Funktionen in unserer Funktionssuite beschreiben, wird sehr ähnlich der Art sein, wie wir Akzeptanzberichte schreiben würden (unter Verwendung eines automatisierten Browser-Frameworks) und als solche eine Menge von Duplikationen verursachen würden.
Solange die Geschichten das Verhalten aus Sicht des Kunden beschreiben, dienen sie als Akzeptanzgeschichten. Wir werden den Symfony DomCrawler verwenden, um die Ausgabe unserer Anwendung zu testen. Später in der Serie werden wir möglicherweise feststellen, dass wir einen tatsächlichen Browser testen müssen, der auch JavaScript ausführen kann. Das Testen über den Browser fügt einige neue Probleme hinzu, da wir sicherstellen müssen, dass die Testumgebung stündlich geladen wird, wenn die Suite ausgeführt wird.
In unserer Funktionssuite haben wir Zugriff auf die Laravel-Anwendung, was sehr praktisch ist. Zum einen ist es einfach, zwischen den Umgebungen zu unterscheiden. Zweitens: Wenn Sie keinen Browser verwenden, wird unsere Testsuite wesentlich schneller. Wann immer wir eine neue Funktion implementieren möchten, schreiben wir mit Behat eine Geschichte in unsere Funktionssuite.
Unsere Integrationssuite testet das Verhalten des Kerns unserer Anwendung, der nicht unbedingt Zugriff auf Laravel haben muss. Die Integrationssuite besteht normalerweise aus einer Mischung aus Behat-Stories und PhpSpec-Spezifikationen.
Unsere Komponententests werden in PhpSpec geschrieben und prüfen isolierte kleine Einheiten des Anwendungskerns. Unsere Entitäten, Wertobjekte usw. haben alle Spezifikationen.
In dieser Serie werden wir ab dem nächsten Artikel ein System zur Zeitverfolgung aufbauen. Wir beginnen damit, das Verhalten von außen zu beschreiben, indem wir Verhaltensmerkmale schreiben. Das interne Verhalten unserer Anwendung wird anhand von PhpSpec beschrieben.
Zusammen werden diese beiden Tools dazu beitragen, dass wir uns mit der Qualität der von uns erstellten Anwendung vertraut machen. Wir werden Features und Spezifikationen hauptsächlich auf drei Ebenen schreiben:
In unserer Funktionssuite werden wir die HTTP-Antworten unserer Anwendung in einem Headless-Modus crawlen, was bedeutet, dass wir nicht durch den Browser gehen. Dadurch wird es einfacher, mit Laravel zu interagieren, und unsere Funktionssuite kann auch als Akzeptanzschicht dienen.
Wenn wir später eine kompliziertere Benutzeroberfläche haben und möglicherweise auch JavaScript testen müssen, fügen wir möglicherweise eine dedizierte Akzeptanz-Suite hinzu. Diese Serie ist noch in Bearbeitung, also können Sie Ihre Vorschläge in den Kommentaren abgeben.
Für dieses Tutorial nehme ich an, dass Sie Laravel (4.2) neu installiert haben. Vorzugsweise verwenden Sie auch Laravel Homestead, was ich auch beim Schreiben dieses Codes verwendet habe.
Bevor wir mit der eigentlichen Arbeit beginnen, stellen wir sicher, dass Behat und PhpSpec installiert sind. Zuerst mache ich aber gerne ein bisschen Reinigung, wenn ich ein neues Laravel-Projekt starte und die Sachen lösche, die ich nicht brauche:
git rm -r app / tests / phpunit.xml CONTRIBUTING.md
Wenn Sie diese Dateien löschen, stellen Sie sicher, dass Sie Ihre composer.json
Datei entsprechend:
"autoload": "classmap": ["App / Befehle", "App / Controller", "App / Modelle", "App / Datenbank / Migrationen", "App / Datenbank / Seeds"],
Und natürlich:
$ composer dump-autoload
Jetzt können wir die BDD-Tools einholen, die wir benötigen. Fügen Sie einfach eine Anforderungs-dev
Abschnitt zu Ihrem composer.json
:
"required": "laravel / framework": "4.2. *", "required-dev": "behat / behat": "~ 3.0", "phpspec / phpspec": "~ 2.0", "phpunit / phpunit ":" ~ 4.1 ",
"Warum ziehen wir PHPUnit ein?" denkst du vielleicht? Wir werden in dieser Serie keine guten alten PHPUnit-Testfälle schreiben, aber die Behauptungen sind zusammen mit Behat ein praktisches Werkzeug. Wir werden das später in diesem Artikel sehen, wenn wir unser erstes Feature schreiben.
Denken Sie daran, Ihre Abhängigkeiten nach der Änderung zu aktualisieren composer.json
:
$ composer update --dev
Wir sind fast fertig mit der Installation und Einrichtung. PhpSpec funktioniert sofort:
$ vendor / bin / phpspec run 0 Spezifikationen 0 Beispiele 0ms
Aber Behat muss schnell mit dem laufen --drin
Option um alles einzurichten:
$ vendor / bin / behat --init + d features - platziere deine * .feature-Dateien hier + d features / bootstrap - platziere deine Kontextklassen hier + f features / bootstrap / FeatureContext.php - platziere deine Definitionen, Transformationen und Hooks hier $ Anbieter / bin / behat Keine Szenarien Keine Schritte 0m0.14s (12.18Mb)
Der erste Befehl erzeugte ein glänzendes neues FeatureContext
Klasse, in der wir die für unsere Funktionen erforderlichen Schrittdefinitionen schreiben können:
Unser erstes Feature schreiben
Unser erstes Feature wird sehr einfach sein: Wir werden einfach sicherstellen, dass unsere neue Laravel-Installation uns mit einem "Sie sind angekommen" begrüßt. auf der Homepage. Ich habe mich ziemlich dumm gestellt
Gegeben
Schritt auch,Angenommen, ich bin eingeloggt
, Das zeigt nur, wie einfach die Interaktion mit Laravel in unseren Funktionen ist.Technisch gesehen würde ich diese Art von Funktion als Funktionstest einordnen, da sie mit dem Framework interagiert, sie dient jedoch auch als Abnahmetest, da wir keine anderen Ergebnisse sehen würden, wenn ein ähnlicher Test durch ein Browsertesttool ausgeführt wird. Fürs Erste bleiben wir bei unserer Funktionstestsuite.
Gehen Sie voran und erstellen Sie eine
welcome.feature
Datei und legen Sie es einFunktionen / funktional
:# features / function / welcome.feature Feature: Begrüßung des Entwicklers Als Laravel-Entwickler Um ein neues Projekt zu beginnen, muss ich bei der Ankunft begrüßt werden. Szenario: Begrüßung des Entwicklers auf der Homepage Gegeben, ich bin eingeloggt, wenn ich "/" besuche sollte sehen "Sie sind angekommen."Indem Sie die Funktionsmerkmale in a
funktionell
Verzeichnis wird es uns leichter fallen, unsere Suiten später zu verwalten. Wir möchten keine Integrationstyp-Funktionen, bei denen Laravel nicht auf die langsame Funktionsgruppe warten muss.Ich mag es, die Dinge schön und sauber zu halten. Ich glaube, wir sollten einen speziellen Funktionskontext für unsere Funktionssuite haben, der uns den Zugang zu Laravel ermöglicht. Sie können einfach das Vorhandene kopieren
FeatureContext
Datei und ändern Sie den Klassennamen inLaravelFeatureContext
. Damit dies funktioniert, brauchen wir auch einebehat.yml
Konfigurationsdatei.Erstellen Sie eine im Stammverzeichnis Ihres Projekts und fügen Sie Folgendes hinzu:
Standard: Suites: Funktional: Pfade: [% paths.base% / features / funktional] Kontexte: [LaravelFeatureContext]Ich denke, die YAML hier ist ziemlich selbsterklärend. Unsere Funktionssuite sucht nach Funktionen im
funktionell
Verzeichnis und führen Sie sie durchLaravelFeatureContext
.Wenn wir versuchen, Behat an dieser Stelle auszuführen, werden wir die notwendigen Schrittdefinitionen implementieren. Wir können Behat die leeren Gerüstmethoden zum hinzufügen
LaravelFeatureContext
mit dem folgenden Befehl:$ vendor / bin / behat --dry-run --append-snippets $ vendor / bin / behat Feature: Begrüßung des Entwicklers Als Laravel-Entwickler Damit ich ein neues Projekt beginnen kann, muss ich mich über ein entsprechendes Projekt freuen homepage # features / funktional / welcome.feature: 6 Angenommen Ich bin eingeloggt # LaravelFeatureContext :: iAmLoggedIn () TODO: schreibe Definition beim Besuch "/" # LaravelFeatureContext :: iVisit () Dann sehe ich "Du bist angekommen. " # LaravelFeatureContext :: iShouldSee () 1 Szenario (1 ausstehend) 3 Schritte (1 ausstehend, 2 übersprungen) 0m0.28s (12.53Mb)Und jetzt, wie Sie an der Ausgabe sehen können, können wir mit dem ersten Schritt beginnen:
Angenommen, ich bin eingeloggt
.Der mit Laravel gelieferte PHPUnit-Testfall ermöglicht es uns, solche Dinge zu tun
$ this-> be ($ user)
, welcher sich in einem bestimmten Benutzer anmeldet. Letztendlich möchten wir in der Lage sein, mit Laravel so zu interagieren, als würden wir PHPUnit verwenden. Schreiben wir also den Schrittdefinitionscode "Wir wünschten, wir hätten es":/ ** * @Given Ich bin angemeldet * / public function iAmLoggedIn () $ user = neuer Benutzer; $ this-> be ($ user);Das wird natürlich nicht funktionieren, da Behat keine Ahnung von Laravel-spezifischen Dingen hat, aber ich werde Ihnen in einer Sekunde zeigen, wie einfach es ist, Behat und Laravel dazu zu bringen, gut zusammen zu spielen.
Wenn Sie einen Blick in die Laravel-Quelle werfen und die
Illuminate \ Foundation \ Testing \ TestCase
Klasse, der Klasse, von der der Standardtestfall ausgeht, werden Sie feststellen, dass ab Laravel 4.2 alles in ein Merkmal verschoben wurde. DasApplicationTrait
ist jetzt verantwortlich für das booten einesAnwendung
B. einen HTTP-Client einrichten und geben Sie uns einige Hilfsmethoden, wie zSein()
.Das ist ziemlich cool, vor allem weil es bedeutet, dass wir es einfach in unseren Behat-Kontext ziehen können, ohne dass ein Setup nötig ist. Wir haben auch Zugriff auf die
AssertionsTrait
, Dies ist jedoch immer noch an PHPUnit gebunden.Wenn wir die Eigenschaft einnehmen, müssen wir zwei Dinge tun. Wir müssen eine haben
Konfiguration()
Methode, wie die in derIlluminate \ Foundation \ Testing \ TestCase
Klasse, und wir brauchen einecreateApplication ()
Methode, wie im Standardtestfall von Laravel. Eigentlich können wir diese beiden Methoden einfach kopieren und direkt verwenden.Es gibt nur eines zu beachten: In PHPUnit die Methode
Konfiguration()
wird vor jedem Test automatisch aufgerufen. Um das gleiche in Behat zu erreichen, können wir das verwenden@BeforeScenario
Anmerkung.Fügen Sie Folgendes zu Ihrem hinzu
LaravelFeatureContext
:Verwenden Sie Illuminate \ Foundation \ Testing \ ApplicationTrait; / ** * Behat Kontextklasse. * / class LaravelFeatureContext implementiert SnippetAcceptingContext / ** *. Verantwortlich für die Bereitstellung einer Laravel-Anwendungsinstanz. * / use ApplicationTrait; / ** * Initialisiert den Kontext. * * Jedes Szenario erhält ein eigenes Kontextobjekt. * Sie können beliebige Argumente über behat.yml an den Kontextkonstruktor übergeben. * / public function __construct () / ** * @BeforeScenario * / public function setUp () if (! $ this-> app) $ this-> refreshApplication (); / ** * Erstellt die Anwendung. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / public function createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; Rückkehr erforderlich __DIR __. '/… /… /bootstrap/start.php';Ziemlich einfach und schau, was wir bekommen, wenn wir Behat laufen lassen:
$ vendor / bin / behat Feature: Begrüßungsentwickler Als Laravel-Entwickler Damit ich ein neues Projekt beginnen kann, muss ich mich über arival freuen. Szenario: Begrüßungsentwickler auf der Startseite # features / function / welcome.feature: 6 Angenommen, ich bin angemeldet # LaravelFeatureContext :: iAmLoggedIn () Wenn ich "/" besuche # LaravelFeatureContext :: iVisit () TODO: Schreibe Definition, dann sehe ich "Du bist angekommen." # LaravelFeatureContext :: iShouldSee () 1 Szenario (1 ausstehend) 3 Schritte (1 bestanden, 1 ausstehend, 1 übersprungen) 0m0.73s (17.92Mb)Ein grüner erster Schritt, was bedeutet, dass unser Setup funktioniert!
Als nächstes können wir das implementieren
Wenn ich besuche
Schritt. Dieses ist sehr einfach und wir können das einfach verwendenAnruf()
Methode, dass dieApplicationTrait
bietet. Eine Codezeile bringt uns dorthin:/ ** * @Wenn ich besuche: uri * / public function iVisit ($ uri) $ this-> call ('GET', $ uri);Der letzte Schritt,
Dann sollte ich sehen
, dauert etwas mehr und wir müssen zwei Abhängigkeiten einbeziehen. Wir benötigen PHPUnit für die Assertion und wir benötigen den Symfony DomCrawler, um nach "Sie sind angekommen" zu suchen. Text.Wir können es so umsetzen:
benutze PHPUnit_Framework_Assert als PHPUnit; use Symfony \ Component \ DomCrawler \ Crawler;… / ** * @ Dann sollte ich Folgendes sehen: text * / public function iShouldSee ($ text) $ crawler = neuer Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));Dies ist so ziemlich derselbe Code, den Sie schreiben würden, wenn Sie PHPUnit verwenden. Das
filterXpath ()
Teil ist etwas verwirrend und wir werden uns jetzt nicht darum kümmern, da es ein wenig außerhalb des Rahmens dieses Artikels liegt. Vertrauen Sie mir einfach, dass es funktioniert.Running Behat ein letztes Mal ist eine gute Nachricht:
$ vendor / bin / behat Feature: Begrüßungsentwickler Als Laravel-Entwickler Damit ich ein neues Projekt beginnen kann, muss ich mich auf arival freuen # LaravelFeatureContext :: iAmLoggedIn () Wenn ich "/" # LaravelFeatureContext :: iVisit () besuche, sollte ich "Sie sind angekommen" sehen. # LaravelFeatureContext :: iShouldSee () 1 Szenario (1 bestanden) 3 Schritte (3 bestanden) 0m0.82s (19.46Mb)Die Funktion funktioniert wie erwartet und der Entwickler wird bei der Ankunft begrüßt.
Fazit
Das Ganze
LaravelFeatureContext
sollte jetzt ähnlich aussehen:app) $ this-> refreshApplication (); / ** * Erstellt die Anwendung. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / public function createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; Rückkehr erforderlich __DIR __. '/… /… /bootstrap/start.php'; / ** * @Given Ich bin angemeldet * / public function iAmLoggedIn () $ user = neuer Benutzer; $ this-> be ($ user); / ** * @Wenn ich besuche: uri * / public function iVisit ($ uri) $ this-> call ('GET', $ uri); / ** * @Dann sollte ich Folgendes sehen: text * / public function iShouldSee ($ text) $ crawler = neuer Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));Wir haben jetzt eine wirklich schöne Basis, auf der wir aufbauen können, wenn wir unsere neue Laravel-Anwendung mit BDD weiterentwickeln. Ich hoffe, ich habe Ihnen gezeigt, wie einfach es ist, Laravel und Behat dazu zu bringen, gut zusammen zu spielen.
In diesem ersten Artikel haben wir viele verschiedene Themen angesprochen. Machen Sie sich keine Sorgen, wir werden alles im weiteren Verlauf der Serie vertiefen. Wenn Sie Fragen oder Anregungen haben, hinterlassen Sie bitte einen Kommentar.