Laravel, BDD und Sie Los geht's

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.

Verhalten beschreiben

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:

1. Annahme

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.

2. funktionell

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.

3. Integration

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.

4. Einheit

Unsere Komponententests werden in PhpSpec geschrieben und prüfen isolierte kleine Einheiten des Anwendungskerns. Unsere Entitäten, Wertobjekte usw. haben alle Spezifikationen.

Der Fall

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: 

  1. Funktional
  2. Integration
  3. Einheit


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.

Unser Setup

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 ein Funktionen / 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 in LaravelFeatureContext. Damit dies funktioniert, brauchen wir auch eine behat.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 durch LaravelFeatureContext.

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. Das ApplicationTrait ist jetzt verantwortlich für das booten eines Anwendung B. einen HTTP-Client einrichten und geben Sie uns einige Hilfsmethoden, wie z Sein()

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 eine createApplication () 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 verwenden Anruf()Methode, dass die ApplicationTrait 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.