Ein BDD-Workflow mit Behat und Phpspec
() translation by (you can also view the original English article)
In diesem tutorial werden wir einen Blick auf zwei verschiedene BDD-tools, Behat und phpspec, und sehen Sie, wie Sie können unterstützen Sie in Ihrem Entwicklungsprozess. Lernen BDD kann verwirrend sein. Neue Methodik, neue tools und viele Fragen, wie "was ist zu testen?" und "welche Werkzeuge zu verwenden?". Ich hoffe, dass dies eher einfaches Beispiel wird Ihnen Ideen, wie Sie können, integrieren Sie BDD in Ihre eigenen workflow.
Meine Inspiration
Ich habe inspiriert, dieses tutorial zu schreiben, von Taylor Otwell, der Schöpfer des Laravel-framework. Mehrmals habe ich gehört, Taylor zu erklären, warum er meist nicht tut, TDD/BDD, indem er sagte, dass er gerne zuerst planen die API von seinen code, bevor Sie tatsächlich beginnen, es zu implementieren. Ich habe gehört das von vielen Entwicklern, und jedes mal denke ich mir: "Aber das ist der perfekte Anwendungsfall für TDD/BDD!". Taylor sagt, dass er gerne eine Karte der API, der seinen code, durch den code schreiben, er wünschte, er hatte. Er wird dann mit dem Programmieren anfangen und erst zufrieden sein, wenn er erreicht hat, dass die genauen API. Das argument macht Sinn, wenn man nur testen/speccing auf der unit-Ebene, sondern mithilfe eines Tools wie Behat, Sie beginnen das externe Verhalten der software, das ist im Grunde, soweit ich das verstanden habe, was Taylor will, zu erreichen.
Was Werden Wir über
In diesem tutorial bauen wir eine einfache Konfigurations-Datei, die loader-Klasse. Wir starten mit Taylors Ansatz und dann bei gedrückter Umschalttaste, um ein BDD-Ansatz statt. Die Beispiele sind minimalistisch, aber immer noch müssen wir befürchten, Einbauten, statische Methoden usw.., so all-in-all, ich denke, Sie sollten ausreichen, um zu zeigen, wie Behat und phpspec können sich gegenseitig ergänzen.
Disclaimer: Erstens, dieser Artikel ist nicht der erste-Schritte-guide. Es wird davon ausgegangen grundlegende Kenntnisse in BDD, Behat und phpspec. Sie haben wahrscheinlich schon geschaut in diese Werkzeuge, aber sind immer noch zu kämpfen mit, wie Sie tatsächlich nutzen Sie in Ihrem täglichen workflow. Wenn Sie möchten, Pinsel auf phpspec, werfen Sie einen Blick auf meine-erste-Schritte-tutorial. Sekunde alle, ich bin mit Taylor Otwell als ein Beispiel. Ich weiß nicht, etwas darüber, wie Taylor funktioniert, außer dem, was ich hörte ihn sagen, in podcasts usw.. Ich benutze ihn nur als Beispiel, denn er ist ein genial-Entwickler (er machte Laravel!) und da ist er gut bekannt. Ich hätte auch jemand anderem, da die meisten Entwickler, darunter auch ich, nicht tun BDD die ganze Zeit, noch. Ich bin mir auch nicht sagen, dass der workflow Taylor beschreibt, ist schlecht. Ich denke, es ist eine brillante Idee, um einige Gedanken in Ihren code, bevor Sie tatsächlich, es zu schreiben. Dieses tutorial soll nur zeigen das BDD Weg, dies zu tun.
Taylor ' s Workflow
Lassen Sie uns beginnen, indem Sie einen Blick auf, wie Taylor gehen könnte, über die Gestaltung dieser Konfiguration die Datei loader. Taylor sagt, dass er gerne nur die Feuerwehr eine leere text-Datei in seinem editor und dann schreiben, wie er möchte-Entwickler in der Lage sein, um die Interaktion mit seinem code (API). In eine BDD-Kontext ist das in der Regel bezeichnet als Test das externe Verhalten der software und tools wie Behat sind ideal für diese. Wir sehen, dass diese in kurzer Zeit.
Erste, vielleicht Taylor wird eine Entscheidung über die Konfigurationsdateien. Wie sollten Sie arbeiten? Als in Laravel, lassen Sie es uns einfach zu verwenden einfaches PHP-arrays. Eine Beispiel-Konfigurationsdatei könnte wie folgt Aussehen:
1 |
# config.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
return array( |
6 |
|
7 |
'timezone' => 'UTC', |
8 |
|
9 |
);
|
Weiter, wie soll der code das macht die Verwendung dieser Konfigurations-Datei arbeiten? Lassen Sie uns tun dies in der Taylor Weg und schreiben Sie einfach den code, den wir wünschten, wir hätten:
1 |
$config = Config::load('config.php'); |
2 |
|
3 |
$config->get('timezone'); // returns 'UTC' |
4 |
|
5 |
$config->get('timezone', 'CET'); // returns 'CET' if 'timezone' is not configured |
6 |
|
7 |
$config->set('timezone', 'GMT'); |
8 |
$config->get('timezone'); // returns 'GMT' |
Okay, also das sieht auch ziemlich gut aus. Zuerst haben wir eine statische Aufruf einer load () - Funktion, gefolgt von drei Anwendungsfälle:
- Der "Zeitzone" aus der Konfigurationsdatei.
- Immer ein default-Wert, wenn die "Zeitzone" ist noch nicht konfiguriert.
- Ändern einer Konfiguration option, indem Sie Sie auf etwas anderes. Wir werden die einzelnen use-cases oder Szenarien, mit Behat in kurzer Zeit.
Macht es Sinn, Behat für diese Art von Dingen. Behat wird uns nicht zwingen, um design-Entscheidungen - wir haben phpspec. Wir werden einfach verschieben Sie die Anforderungen, die wir eben beschrieben, in ein Behat-Funktion, um sicherzustellen, dass wir es richtig machen, wenn wir mit dem Bau beginnen. Unsere behat Funktion dienen als Abnahmetests für unsere Anforderungen, so zu sprechen.
Wenn Sie einen Blick auf den code, den wir geschrieben haben, ein weiterer Grund für die Verwendung von Behat, statt nur phpspec, ist die statische nennen. Es ist nicht einfach, um zu testen, statische Methoden, vor allem nicht, wenn man nur mit einem tool wie phpspec. Wir werden sehen, wie wir Vorgehen können, wenn wir beide Behat und phpspec zur Verfügung.
Setup
Vorausgesetzt, Sie sind mit Komponisten, die Einrichtung Behat und phspec ist ein super einfaches zwei-Schritt-Prozess.
Erste, Sie brauchen eine einfache Komponisten.json-Datei. Dies beinhaltet Behat und phpspec, und verwendet psr-4 zu autoload der Klassen:
1 |
{
|
2 |
"require-dev": { |
3 |
"behat/behat": "~3.0", |
4 |
"phpspec/phpspec": "~2.0" |
5 |
},
|
6 |
"autoload": { |
7 |
"psr-4": { |
8 |
"": "src/" |
9 |
}
|
10 |
}
|
11 |
}
|
Laufen composer installieren zu Holen, die Abhängigkeiten.
phpspec benötigt keine Konfiguration in der Reihenfolge ausgeführt, in der Erwägung, dass Behat braucht Sie, um führen Sie den folgenden Befehl zum generieren einer leski:
1 |
$ vendor/bin/behat --init |
Das ist alles was es braucht. Das kann nicht sein, Ihre Entschuldigung dafür, nicht zu tun BDD!
Planung Der Funktionen Mit Behat
So, wir haben nun alles eingerichtet haben, sind wir bereit zu tun beginnen BDD. Da tut sich BDD bedeutet die Installation und Verwendung von Behat und phpspec, richtig?
Soweit ich bin besorgt, wir haben bereits begonnen, BDD. Wir haben effektiv beschrieben, die das externe Verhalten der software. Unsere "Kunden", in diesem Beispiel sind Entwickler, die gehen, um die Interaktion mit unseren code. Von "effektiv" meine ich, dass wir beschrieben haben, das Verhalten in einer Weise, die Sie verstehen. Wir könnten mit dem code, den wir bereits beschrieben, legen Sie es in eine README-Datei, und jeder anständige PHP-Entwickler würde es verstehen, Sie zu nutzen. Also das ist eigentlich ziemlich gut, aber ich habe zwei wichtige Dinge zu beachten. Zuerst von allen, über das Verhalten von software mithilfe von code funktioniert nur in diesem Beispiel, weil die "Kunden" sind Programmierer. Normalerweise sind wir etwas testen, wird von "normalen" Menschen. Eine menschliche Sprache ist besser als PHP, wenn wir wollen, um die Kommunikation mit Menschen. Zweitens, warum nicht automatisieren? Ich werde nicht zu argumentieren, warum dies eine gute Idee ist.
That being said, ich denke, anfangen zu Behat wäre jetzt eine vernünftige Entscheidung.
Mit Behat, möchten wir beschreiben jedes der Szenarien, die wir oben skizziert. Wir wollen nicht zu ausführlich behandelt jeden edge-Fall beteiligten im Umgang mit der software. Wir haben phpspec verfügbar, sollte dies erforderlich sein, um die Fehler zu beheben, auf dem Weg etc. Ich denke, dass viele Entwickler, vielleicht inklusive Taylor, fühlt sich an wie Sie haben, um alles zu überdenken und alles entscheiden, bevor Sie schreiben tests und specs. Das ist, warum Sie wählen, um zu starten, ohne BDD, weil Sie nicht wollen, um zu entscheiden, alles vorher. Dies ist nicht der Fall mit Behat, da wir beschreiben das externe Verhalten und Nutzung. Um Behat zu beschreiben, eine Funktion, wir müssen nicht entscheiden, etwas mehr als im obigen Beispiel mit der Verwendung einer raw-text-Datei. Wir müssen nur definieren die Anforderungen an die Funktion - in diesem Fall die externe API der Konfigurations-Datei, die loader-Klasse.
Nun, lassen Sie uns die oben genannten PHP-code und schalten Sie ihn in eine Behat Funktion, die Verwendung der englischen Sprache (eigentlich mit der Gurke Sprache).
Machen Sie eine Datei in die Eigenschaften/ Verzeichnis namens config.Funktion, und füllen Sie die folgenden Szenarien:
1 |
Feature: Configuration files |
2 |
In order to configure my application |
3 |
As a developer |
4 |
I need to be able to store configuration options in a file |
5 |
|
6 |
Scenario: Getting a configured option |
7 |
Given there is a configuration file |
8 |
And the option 'timezone' is configured to 'UTC' |
9 |
When I load the configuration file |
10 |
Then I should get 'UTC' as 'timezone' option |
11 |
|
12 |
Scenario: Getting a non-configured option with a default value |
13 |
Given there is a configuration file |
14 |
And the option 'timezone' is not yet configured |
15 |
When I load the configuration file |
16 |
Then I should get default value 'CET' as 'timezone' option |
17 |
|
18 |
Scenario: Setting a configuration option |
19 |
Given there is a configuration file |
20 |
And the option 'timezone' is configured to 'UTC' |
21 |
When I load the configuration file |
22 |
And I set the 'timezone' configuration option to 'GMT' |
23 |
Then I should get 'GMT' as 'timezone' option |
In dieser Funktion, die wir beschreiben, von außen, wie die "Entwickler" wären in der Lage, zum speichern von Konfigurations-Optionen. Wir kümmern uns nicht über das interne Verhalten - wir werden, wenn wir anfangen, mit phpspec. Solange diese Funktion ausgeführt wird grün, wir kümmern uns nicht, was passiert hinter den kulissen.
Lasst uns laufen Behat und sehen, was es denkt, von unserer Funktion:
1 |
$ vendor/bin/behat --dry-run --append-snippets |
Mit diesem Befehl sagen wir Behat, fügen Sie den erforderlichen Schritt-Definitionen auf unsere feature-Kontext. Ich gehe nicht viel in die details über Behat, aber dies fügt eine Reihe von leeren Methoden, um unsere FeatureContext Klasse, die Karten zu unserer Funktion oben beschriebenen Schritte.
Als Beispiel nehmen wir einen Blick auf die Schritt-definition, die Behat Hinzugefügt, für die es eine Konfigurationsdatei Schritt, den wir als "Gegeben" Schritt in allen drei Szenarien:
1 |
/**
|
2 |
* @Given there is a configuration file
|
3 |
*/
|
4 |
public function thereIsAConfigurationFile() |
5 |
{
|
6 |
throw new PendingException(); |
7 |
}
|
Nun, alles, was wir tun müssen, ist füllen Sie einige code, um dies zu beschreiben.
Schreiben Von Step-Definitionen
Bevor wir beginnen, habe ich zwei wichtige Punkte zu machen, über den Schritt Definitionen:
- Der Punkt ist, um zu beweisen, dass das Verhalten jetzt, ist nicht so, wie wir es haben wollen. Danach können wir beginnen, zu entwerfen, die unseren code, die meiste Zeit mit phpspec, um auf grün.
- Die Umsetzung der step-Definitionen sind nicht wichtig - wir brauchen nur etwas, das funktioniert und erzielt eine "1". Wir können umgestalten später.
Wenn Sie vendor/bin/behat, werden Sie sehen, dass alle Szenarien, die nun anstehenden Schritte.
Wir beginnen mit dem ersten Schritt-vorausgesetzt es ist eine Konfigurationsdatei. Wir verwenden eine Halterung von der Konfiguration-Datei, so können wir die statische Methode load() später auf. Wir kümmern uns um die statische Methode load (), denn es gibt uns eine schöne API-Config::load(), ähnlich wie die Laravel Fassaden. Dieser Schritt implementiert werden konnte, zahlreiche Möglichkeiten. Für jetzt, ich denke, wir sollten einfach sicherstellen, dass wir die Vorrichtung zur Verfügung steht und dass es ein array enthält:
1 |
/**
|
2 |
* @Given there is a configuration file
|
3 |
*/
|
4 |
public function thereIsAConfigurationFile() |
5 |
{
|
6 |
if ( ! file_exists('fixtures/config.php')) |
7 |
throw new Exception("File 'fixtures/config.php' not found"); |
8 |
|
9 |
$config = include 'fixtures/config.php'; |
10 |
|
11 |
if ( ! is_array($config)) |
12 |
throw new Exception("File 'fixtures/config.php' should contain an array"); |
13 |
}
|
Wir sind auf dem grün mit diesem Schritt, ohne Umsetzung jeglicher code, der neben dem Scheinwerfer. Der Zweck einer Bestimmten Schritt ist, um das system in einem bekannten Zustand. In diesem Fall, dass bedeutet, dass Sie sicher wir haben eine Konfigurations-Datei.
Um zu unserem ersten green Schritt, den wir gerade brauchen, um das fixture:
1 |
# fixtures/config.php |
2 |
<?php
|
3 |
|
4 |
return array(); |
Als Nächstes haben wir ein Und Schritt, in diesem Fall ist nur ein alias für Gegeben. Wir wollen sicherstellen, dass die Konfigurationsdatei enthält eine option für die Zeitzone. Dies ist wieder einmal nur um unsere Befestigung, damit wir uns nicht viel kümmern. Ich knallte die folgenden (hackish -) code zusammen, um dies zu erreichen:
1 |
/**
|
2 |
* @Given the option :option is configured to :value
|
3 |
*/
|
4 |
public function theOptionIsConfiguredTo($option, $value) |
5 |
{
|
6 |
$config = include 'fixtures/config.php'; |
7 |
|
8 |
if ( ! is_array($config)) $config = []; |
9 |
|
10 |
$config[$option] = $value; |
11 |
|
12 |
$content = "<?php\n\nreturn " . var_export($config, true) . ";\n"; |
13 |
|
14 |
file_put_contents('fixtures/config.php', $content); |
15 |
}
|
Der obige code ist nicht schön, aber es leistet, was es braucht. Sie können uns manipulieren, unsere Vorrichtung aus in unserem feature. Wenn Sie Behat, Sie werden sehen, dass Sie fügte hinzu, die "timezone" - option, um die config.php Halterung:
1 |
<?php
|
2 |
|
3 |
return array ( |
4 |
'timezone' => 'UTC', |
5 |
);
|
Jetzt ist die Zeit zu bringen, in der einige der ursprünglichen "Taylor-code"! Der Schritt, Wenn ich laden Sie die Konfigurationsdatei besteht aus code, dass wir uns tatsächlich kümmern. Wir bringen den code aus dem raw-text-Datei von früher, und stellen Sie sicher, dass es läuft:
1 |
/**
|
2 |
* @When I load the configuration file
|
3 |
*/
|
4 |
public function iLoadTheConfigurationFile() |
5 |
{
|
6 |
$this->config = Config::load('fixtures/config.php'); // Taylor! |
7 |
}
|
Läuft Behat, natürlich wird dies fehlschlagen, da die Config noch nicht vorhanden. Lassen Sie uns phpspec ist die Rettung!
Die Gestaltung Mit Phpspec
Wenn wir laufen, Behat, bekommen wir einen fatalen Fehler:
1 |
PHP Fatal error: Class 'Config' not found...
|
Zum Glück haben wir phpspec zur Verfügung, einschließlich seiner genial-code-Generatoren:
1 |
$ vendor/bin/phpspec desc "Config" |
2 |
Specification for Config created in .../spec/ConfigSpec.php. |
3 |
|
4 |
$ vendor/bin/phpspec run --format=pretty |
5 |
Do you want me to create `Config` for you? y |
6 |
|
7 |
$ vendor/bin/phpspec run --format=pretty |
8 |
|
9 |
Config |
10 |
|
11 |
10 ✔ is initializable |
12 |
|
13 |
|
14 |
1 specs |
15 |
1 examples (1 passed) |
16 |
7ms |
Mit diesen Befehlen phpspec erstellt die folgenden zwei Dateien für uns:
1 |
spec/ |
2 |
`-- ConfigSpec.php |
3 |
src/ |
4 |
`-- Config.php |
Dies hat uns befreien von den ersten fatalen Fehler, aber Behat ist noch nicht ausgeführt:
1 |
PHP Fatal error: Call to undefined method Config::load() in ... |
load() ist eine statische Methode und als solche, ist nicht leicht specced mit phpspec. Aus zwei Gründen ist dies OK, aber:
- Das Verhalten der load () - Methode ist sehr einfach. Wenn wir mehr Komplexität später, können wir extrahieren Logik zu kleinen überprüfbare Methoden.
- Das Verhalten, wie jetzt, gut abgedeckt genug von Behat. Wenn die Methode nicht laden Sie die Datei in ein array richtig, Behat wird squirk bei uns.
Dies ist eine jener Situationen, in denen eine Menge Entwickler die Wand schlagen. Sie wird wegwerfen, phpspec und schließen daraus, dass es nervt und arbeitet gegen Sie. Aber, sehen Sie, wie schön Behat und phpspec ergänzen sich hier?
Anstatt zu versuchen, um 100% Abdeckung mit phpspec, lassen Sie uns nur eine einfache Implementierung der load () - Funktion und zuversichtlich sein, dass es ist bedeckt durch Behat:
1 |
<?php
|
2 |
|
3 |
class Config |
4 |
{
|
5 |
protected $settings; |
6 |
|
7 |
public function __construct() |
8 |
{
|
9 |
$this->settings = array(); |
10 |
}
|
11 |
|
12 |
public static function load($path) |
13 |
{
|
14 |
$config = new static(); |
15 |
|
16 |
if (file_exists($path)) |
17 |
$config->settings = include $path; |
18 |
|
19 |
return $config; |
20 |
}
|
21 |
}
|
Wir sind ziemlich zuversichtlich, dass unsere Konfiguration-Optionen sind nun geladen. Wenn nicht, wird der rest der Schritte fehl, und wir können uns in diesen wieder.
Gebäude die Funktion Mit dem Iteration
Wieder grün, die sowohl mit Behat und phpspec, können wir nun zu der nächsten Funktion Schritt Dann sollte ich mich 'UTC' als 'timezone' option.
1 |
/**
|
2 |
* @Then I should get :value as :option option
|
3 |
*/
|
4 |
public function iShouldGetAsOption($value, $option) |
5 |
{
|
6 |
$actual = $this->config->get($option); // Taylor! |
7 |
|
8 |
if ( ! strcmp($value, $actual) == 0) |
9 |
throw new Exception("Expected {$actual} to be '{$option}'."); |
10 |
}
|
In diesem Schritt werden wir mehr schreiben, dass der code, den wir wollen, den wir hatten. Läuft Behat werden wir allerdings sehen, dass wir nicht über eine get () - Methode zur Verfügung:
1 |
PHP Fatal error: Call to undefined method Config::get() in ... |
Es ist Zeit, um wieder zu phpspec und Sortieren diese aus.
Testen Accessoren und Mutatoren, auch bekannt als Getter und setter, ist fast wie die alte Huhn-oder-ei-dillemma. Wie können wir testen, die get () - Methode auf, wenn wir noch nicht über die set () - Methode und vice versa. Wie ich neigen dazu, gehen über das ist, testen Sie einfach beide auf einmal. Das heißt, wir sind eigentlich Los, um die Funktionalität zu implementieren um eine Konfigurations-option, obwohl wir nicht erreichen, dass Szenario noch.
Das folgende Beispiel sollte das tun:
1 |
function it_gets_and_sets_a_configuration_option() |
2 |
{
|
3 |
$this->get('foo')->shouldReturn(null); |
4 |
|
5 |
$this->set('foo', 'bar'); |
6 |
|
7 |
$this->get('foo')->shouldReturn('bar'); |
8 |
}
|
Zuerst, wir haben die phpspec Generatoren helfen uns loslegen:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
Do you want me to create `Config::get()` for you? y |
3 |
|
4 |
$ vendor/bin/phpspec run --format=pretty |
5 |
Do you want me to create `Config::set()` for you? y |
6 |
|
7 |
$ vendor/bin/phpspec run --format=pretty |
8 |
|
9 |
Config |
10 |
|
11 |
10 ✔ is initializable |
12 |
15 ✘ gets and sets a configuration option |
13 |
expected "bar", but got null.
|
14 |
|
15 |
... |
16 |
|
17 |
1 specs |
18 |
2 examples (1 passed, 1 failed) |
19 |
9ms |
Nun, kommen wir zurück zu grün:
1 |
public function get($option) |
2 |
{
|
3 |
if ( ! isset($this->settings[$option])) |
4 |
return null; |
5 |
|
6 |
return $this->settings[$option]; |
7 |
}
|
8 |
|
9 |
public function set($option, $value) |
10 |
{
|
11 |
$this->settings[$option] = $value; |
12 |
}
|
Und dort gehen wir:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
|
8 |
|
9 |
1 specs |
10 |
2 examples (2 passed) |
11 |
9ms |
Da kam uns ein langer Weg. Läuft Behat, sehen wir, dass wir gut sind in dem zweiten Szenario jetzt. Als Nächstes benötigen wir implementieren die Standard-option feature, da get() nur null zurückgeben, gerade jetzt.
Ersten Schritt ist ähnlich wie der, den wir früher schrieb. Anstelle der Zugabe die Möglichkeit, das array, werden wir unset:
1 |
/**
|
2 |
* @Given the option :option is not yet configured
|
3 |
*/
|
4 |
public function theOptionIsNotYetConfigured($option) |
5 |
{
|
6 |
$config = include 'fixtures/config.php'; |
7 |
|
8 |
if ( ! is_array($config)) $config = []; |
9 |
|
10 |
unset($config[$option]); |
11 |
|
12 |
$content = "<?php\n\nreturn " . var_export($config, true) . ";\n"; |
13 |
|
14 |
file_put_contents('fixtures/config.php', $content); |
15 |
}
|
Das ist nicht schön. Ich weiß! Wir könnten sicherlich umgestalten, da wiederholen wir uns, aber das ist nicht der Umfang dieses Tutorials.
Die zweite Funktion Schritt auch vertraut aussieht, ist meist Kopie und paste von früher:
1 |
/**
|
2 |
* @Then I should get default value :default as :option option
|
3 |
*/
|
4 |
public function iShouldGetDefaultValueAsOption($default, $option) |
5 |
{
|
6 |
$actual = $this->config->get($option, $default); // Taylor! |
7 |
|
8 |
if ( ! strcmp($default, $actual) == 0) |
9 |
throw new Exception("Expected {$actual} to be '{$default}'."); |
10 |
}
|
get() null zurückgeben. Lass uns springen über phpspec und schreiben Sie ein Beispiel, um dieses Problem zu lösen:
1 |
function it_gets_a_default_value_when_option_is_not_set() |
2 |
{
|
3 |
$this->get('foo', 'bar')->shouldReturn('bar'); |
4 |
|
5 |
$this->set('foo', 'baz'); |
6 |
|
7 |
$this->get('foo', 'bar')->shouldReturn('baz'); |
8 |
}
|
Zunächst überprüfen wir, dass wir erhalten den default-Wert, wenn "option" ist noch nicht konfiguriert. Zweitens, stellen wir sicher, dass die Standard option nicht überschreiben konfiguriert option.
Auf den ersten Blick, phpspec mag wie overkill in diesem Fall, da wir fast die Prüfung die gleiche Sache mit Behat schon. Ich mag zu verwenden, phpspec spec den Rand-Fällen ist jedoch, welche Art von stillschweigend in das Szenario. Und auch die code-Generatoren von phpspec sind wirklich toll. Ich benutze Sie für alles, und ich finde mich schneller, wenn ich mich mit phpspec.
Nun, phpspec bestätigt, was Behat uns bereits gesagt:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
24 ✘ gets a default value when option is not set
|
8 |
expected "bar", but got null. |
9 |
|
10 |
... |
11 |
|
12 |
1 specs |
13 |
3 examples (2 passed, 1 failed) |
14 |
9ms |
Um zurück auf grün, wir fügen eine "vorzeitige Rückgabe" an die get () - Methode:
1 |
public function get($option, $defaultValue = null) |
2 |
{
|
3 |
if ( ! isset($this->settings[$option]) and ! is_null($defaultValue)) |
4 |
return $defaultValue; |
5 |
|
6 |
if ( ! isset($this->settings[$option])) |
7 |
return null; |
8 |
|
9 |
return $this->settings[$option]; |
10 |
}
|
Wir sehen, dass phpspec ist nun glücklich:
1 |
$ vendor/bin/phpspec run --format=pretty |
2 |
|
3 |
Config |
4 |
|
5 |
10 ✔ is initializable |
6 |
15 ✔ gets and sets a configuration option |
7 |
24 ✔ gets a default value when option is not set
|
8 |
|
9 |
|
10 |
1 specs |
11 |
3 examples (3 passed) |
12 |
9ms |
Und so ist Behat, und so sind wir.
Wir sind fertig mit unserem zweiten Szenario und ein haben Links zu gehen. Für das Letzte Szenario, wir müssen nur schreiben die Schritt-definition für die Und ich die 'timezone' Konfigurations-option 'GMT' Schritt:
1 |
/**
|
2 |
* @When I set the :option configuration option to :value
|
3 |
*/
|
4 |
public function iSetTheConfigurationOptionTo($option, $value) |
5 |
{
|
6 |
$this->config->set($option, $value); // Taylor! |
7 |
}
|
Da wir bereits umgesetzt werden, die die set () - Methode ist dieser Schritt schon grün:
1 |
$ vendor/bin/behat
|
2 |
Feature: Configuration files |
3 |
In order to configure my application |
4 |
As a developer |
5 |
I need to be able to store configuration options in a file
|
6 |
|
7 |
Scenario: Getting a configured option # features/config.feature:6
|
8 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
9 |
And the option 'timezone' is configured to 'UTC' # FeatureContext::theOptionIsConfiguredTo() |
10 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
11 |
Then I should get 'UTC' as 'timezone' option # FeatureContext::iShouldGetAsOption() |
12 |
|
13 |
Scenario: Getting a non-configured option with a default value # features/config.feature:12
|
14 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
15 |
And the option 'timezone' is not yet configured # FeatureContext::theOptionIsNotYetConfigured() |
16 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
17 |
Then I should get default value 'CET' as 'timezone' option # FeatureContext::iShouldGetDefaultValueAsOption() |
18 |
|
19 |
Scenario: Setting a configuration option # features/config.feature:18
|
20 |
Given there is a configuration file # FeatureContext::thereIsAConfigurationFile()
|
21 |
And the option 'timezone' is configured to 'UTC' # FeatureContext::theOptionIsConfiguredTo() |
22 |
When I load the configuration file # FeatureContext::iLoadTheConfigurationFile()
|
23 |
And I set the 'timezone' configuration option to 'GMT' # FeatureContext::iSetTheConfigurationOptionTo() |
24 |
Then I should get 'GMT' as 'timezone' option # FeatureContext::iShouldGetAsOption() |
25 |
|
26 |
3 scenarios (3 passed) |
27 |
13 steps (13 passed) |
28 |
0m0.04s (8.92Mb) |
Einpacken
Alles ist schön und grün, so lassen Sie uns einen kurzen wrap-up und sehen, was wir erreicht haben.
Wir haben effektiv beschrieben, die das externe Verhalten eines configuration file loader, zuerst mit Taylors Ansatz und dann mit einem traditionellen BDD-Ansatz. Als Nächstes implementieren wir die Funktion, mit phpspec zu entwerfen und zu beschreiben, die das interne Verhalten. Das Beispiel, das wir gearbeitet haben, ist ziemlich einfach, aber wir haben die Grundlagen abgedeckt. Wenn wir mehr Komplexität, wir können nur erweitern, was wir schon haben. Mit BDD, wir haben mindestens drei Optionen:
- Wenn wir beobachten, wie ein bug oder ändern müssen, einige Interna unserer software, können wir beschreiben, dass die Verwendung von phpspec. Schreiben Sie einen fehlerhaften Beispiel, zeigt die Fehler auf und schreiben den code, der notwendig ist, um auf grün.
- Wenn wir brauchen, um hinzuzufügen ein neues use case-auf das, was wir haben, können wir hinzufügen, ein Szenario zu config.Funktion. Wir können dann iterativ arbeiten unseren Weg durch jedem Schritt, mit Behat und phpspec.
- Wenn wir implementieren müssen, um eine neue Funktion, wie das unterstützen YAML config-Dateien, können wir schreiben ein ganz neues feature, und beginnen, über die Vorgehensweise haben wir in diesem tutorial.
Mit diesem Basis-setup, wir haben keine Ausreden, nicht zu schreiben einer fehlerhaften test oder spec, bevor wir schreiben unseren code. Das, was wir aufgebaut haben ist jetzt abgedeckt durch Prüfungen, die machen es viel einfacher, mit zu arbeiten, es in der Zukunft. Hinzu kommt, dass unser code auch vollständig dokumentiert. Die bestimmungsgemäße Verwendung sind Fälle beschrieben, in einfachem Englisch und die internen Abläufe sind wie in den specs. Diese beiden Dinge machen es zu einem Kinderspiel, für andere Entwickler zu verstehen und arbeiten mit der codebase.
Es ist meine Hoffnung, dass dieses tutorial hat Euch geholfen, besser zu verstehen, wie BDD verwendet werden kann in eine PHP-Kontext, mit Behat und phpspec. Wenn Sie Fragen oder Kommentare haben, bitte postet Sie unten in den Kommentaren.
Vielen Dank für mitlesen!