Verhaltensorientierte Entwicklung mit Behat

Bei der verhaltensorientierten Entwicklung (engl.: behavior driven development) schreibt man vom Menschen lesbare Abläufe, die das Verhalten der Anwendung beschreiben. Die Funktionalität des Programms kann dann automatisch mit diesen Abläufen getestet werden. Wir stellen Ihnen hier das Tool Behat vor, mit dessen Hilfe man genau dies in PHP umsetzen kann.

Angenommen es soll eine Funktion in einer PHP-Anwendung implementiert werden, welche die Summe aus zwei Zahlen ausgibt. Diese Funktionalität könnte man dann so beschreiben:

Wenn zwei Zahlen eingegeben werden, soll die Summe aus diesen beiden Zahlen ausgegeben werden.

Wäre es nicht fantastisch, wenn man diese Funktionalität einfach so aufschreiben könnte und diese dann ausgeführt und sogar getestet würde ? Genau das macht Behat. Doch zunächst müssen wir die Software auch installieren.

Installation

Der einfachste Weg Behat zu installieren, ist Composer zu nutzen. Erstellen Sie zunächst eine neue Datei mit dem Namen composer.json in Ihrem Projektverzeichnis und schreiben Sie dort folgendes hinein:

{
    "require": {
        "behat/behat": "2.4.*@stable"
    },
    "minimum-stability": "dev",
    "config": {
        "bin-dir": "bin/"
    }
}

Anschließend muss Composer heruntergeladen werden. Wenn Sie Curl nutzen, geben Sie folgendes in Ihr Kommandozeilentool ein:

curl http://getcomposer.org/installer | php

Sollte Curl bei Ihnen nicht funktionieren, können Sie Composer auch mit PHP herunterladen. Dazu geben Sie folgendes ein:

php -r "readfile('https://getcomposer.org/installer');" | php

Falls Sie Windows nutzen, vergewissern Sie sich, dass der Pfad zu Ihrer php.exe in den Umgebungsvariablen unter PATH eingetragen wurde. Anschließend installieren Sie Composer mit folgendem Befehl:

php composer.phar install

Die Installation kann einige Sekunden dauern. Sollte alles geklappt haben, sehen Sie nun einen Ordner mit dem Namen bin in Ihrem Projektverzeichnis.
Auch den Pfad zu diesem Ordner müssen Windows-Nutzer in den Umgebungsvariablen unter PATH eintragen, damit Behat überall genutzt werden kann. Zum Schluss müssen Sie Behat noch mitteilen, dass es in Ihrem Projektverzeichnis initialisiert werden soll. Dies machen Sie mit folgendem Befehl:

behat --init

Behat hat nun den Ordner features für Sie angelegt, in welchem wir nun im weiteren Verlauf arbeiten werden.

Funktionalität definieren

Bei der Nutzung von Behat fängt man stets damit an, eine Funktionalität zu definieren, welche mann dann beschreibt und implementiert. In unserem Beispiel geht es um eine Funktion, welche die Summe aus zwei Zahlen ausgibt. Wir erstellen also im Ordner features eine neue Datei mit dem Namen gebesummeaus.feature. Achten Sie auf die besondere Dateiendung. Diese ist immer .feature. Danach schreiben Sie folgendes in die Datei hinein:

# language: de

# features/gebesummeaus.feature
Funktionalität: Ausgabe einer Summe
Wenn zwei Zahlen eingegeben werden 
soll die Summe aus diesen beiden Zahlen ausgegeben werden

Jede Funktionalität wird in diesem Format angegeben: eine erste Zeile, in der das Feature genannt wird und danach ein paar Zeilen, in denen es näher beschrieben wird. Dieser gesamte Absatz ist zwar zwingend notwendig, damit Behat funktioniert, jedoch hat der Inhalt und wie er geschrieben ist keine weitere Bedeutung für Behat oder das Testen. Davon abgesehen sollte es jedoch für Menschen gut lesbar sein. In unserem Fall nutzen wir Behat mit der deutschen Übersetzung. Dazu muss am Anfang der Datei # language: de eingetragen werden. Achten Sie auch unbedingt darauf, dass die Datei mit der Zeichenkodierung UTF-8 gespeichert wird.

Szenario definieren

Als nächstes tragen Sie in der gleichen Datei unter der Definition der Funktionalität das Szenario ein. Dies sollte in etwa so aussehen:

Szenario: Gebe die summe aus zwei Zahlen aus
  Gegeben sei Nummer Eins: "10"
  Und Nummer Zwei: "15"
  Wenn ich die Funktion "summe" ausführe
  Dann sollte ich die Zahl "25" erhalten

Jede Funktionalität wird durch ein oder mehrere Szenarien definiert, welche beschreiben, wie sich das Programm unter bestimmten Bedingungen verhalten soll. Diesen Abschnitt verwendet Behat dazu, die automatischen Tests zu erstellen. Achten Sie unbedingt darauf, die wichtigen Elemente in Anführungszeichen zu schreiben. Wir erklären später, warum das gemacht werden muss. Szenarien werden in Behat immer wie folgt dargestellt:

Szenario: Beschreibung
  Gegeben Sei/Angenommen [Kontext]
  Wenn [Ereignis]
  Dann [Ausgang]

Alle Teile eines Szenarios können ausserdem durch die Schlüsselwörter Und und Aber erweitert werden:

Gegeben Sei/Angenommen [Kontext]
  Und  [Mehr Kontext]
  Wenn [Ereignis]
  Und  [Weiteres Ereignis]
  Dann [Ausgang]
  Und  [Weiterer Ausgang]
  Aber [Weiterer Ausgang]

Im Prinzip spielt es keine Rolle, welche dieser Schlüsselwörter Sie für was benutzen. Sie sind alle austauschbar und haben für Behat exakt die selbe Bedeutung. Hier geht es wieder um die bessere Lesbarkeit. Führen Sie nun Behat mit folgendem Befehl in Ihrem Projektverzeichnis aus:

behat

Sie sollten nun folgende Ausgabe sehen können:

Funktionalität: Gebe die Summe aus zwei Zahlen aus
Wenn zwei Zahlen eingegeben werden, soll die Summe ausgegeben werden.

  Szenario: Gebe die summe aus zwei Zahlen aus # features\gebesummeaus.feature:7

    Gegeben sei Nummer Eins: "10"
    Und Nummer Zwei: "15"
    Wenn ich die Funktion "summe" ausführe
    Dann sollte ich die Zahl "25" erhalten

1 scenario (1 undefined)
4 steps (4 undefined)
0m0.028s

You can implement step definitions for undefined steps with these snippets:

    /**
     * @Given /^Nummer Eins: "([^"]*)"$/
     */
    public function nummerEins($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^Nummer Zwei: "([^"]*)"$/
     */
    public function nummerZwei($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^ich die Funktion "([^"]*)" ausführe$
     */
    public function ichDieFunktionAusfuhre($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^sollte ich die Zahl "([^"]*)" erhalte
     */
    public function sollteIchDieZahlErhalten($arg1)
    {
        throw new PendingException();
    }

Schritte definieren

Behat sucht beim Ausführen automatisch nach Dateien im Ordner features mit der selben Endung und führt diese aus. Allerdings haben wir noch nicht festgelegt, was Behat nun mit den Statements in dieser Datei anfangen soll. Behat erstellt für jedes Statement im Szenario einen regulären Ausdruck, den mann dann noch anpassen kann. Diese regulären Ausdrücke werden anhand der Wörter oder Zahlen erstellt, die in Hochkommata stehen. Deshalb muss unbedingt an die Setzung derselben gedacht werden. Da Behat diese regulären Ausdrücke bereits für uns erstellt hat, kopieren wir den gesamten Teil mit den Funktionen und tragen sie in die Datei features/bootstrap/FeatureContext.php ein. Diese sollte danach in etwa wie folgt aussehen:

/**
 * Features context.
 */
class FeatureContext extends BehatContext
{
    /**
     * @Given /^Nummer Eins: "([^"]*)"$/
     */
    public function nummerEins($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^Nummer Zwei: "([^"]*)"$/
     */
    public function nummerZwei($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^ich die Funktion "([^"]*)" ausführe$/
     */
    public function ichDieFunktionAusfuhre($arg1)
    {
        throw new PendingException();
    }

    /**
     * @Given /^sollte ich die Zahl "([^"]*)" erhalten$/
     */
    public function sollteIchDieZahlErhalten($arg1)
    {
        throw new PendingException();
    }
}

Schritte anpassen

Die einzelnen Schritte müssen nun nur noch an unsere Funktionalität angepasst werden. Am Ende könnte das Ganze dann ungefähr so aussehen:

/**
 * Features context.
 */
class FeatureContext extends BehatContext
{
    private $nummer1;
    private $nummer2;
    private $summe;

    /**
     * Berechnet die Summe aus zwei Zahlen
     * @param int
     * @return int
     */
    public function summe($nummer1, $nummer2)
    {
        $this->summe = $nummer1 + $nummer2;
    }

    /**
     * @Given /^Nummer Eins: "([^"]*)"$/
     */
    public function nummerEins($nummer1)
    {
        $this->nummer1 = $nummer1;
    }

    /**
     * @Given /^Nummer Zwei: "([^"]*)"$/
     */
    public function nummerZwei($nummer2)
    {
        $this->nummer2 = $nummer2;
    }

    /**
     * @When /^ich die Funktion "([^"]*)" ausführe$/
     */
    public function ichDieFunktionAusfuhre($funktionsname)
    {
        $this->$funktionsname($this->nummer1, $this->nummer2);
    }

    /**
     * @Then /^sollte ich die Zahl "([^"]*)" erhalten$/
     */
    public function sollteIchDieZahlErhalten($summe)
    {
        if ((int) $summe !== $this->summe) {
            throw new Exception(
                "Die Funktion sum gibt leider nicht die Summe zweier Zahlen aus :(\n" .
                "Stattdessen wurde dieses Ergebnis ausgegeben: " . $this->summe
            );
        }
    }
}

Ausführung

Führen Sie nun wieder Behat aus und schauen Sie, was passiert. Wenn alles geklappt hat, erhalten Sie folgende Ausgabe:

Funktionalität: Die Summe zweier Zahlen
  Wenn zwei Zahlen eingegeben werden, soll die Summe aus diesen beiden Zahlen ausgegeben werden.

  Szenario: Gebe die summe aus zwei Zahlen aus # features\gebesummeaus.feature:7
    Gegeben sei Nummer Eins: "10"              # FeatureContext::nummerEins()
    Und Nummer Zwei: "15"                      # FeatureContext::nummerZwei()
    Wenn ich die Funktion "summe" ausführe     # FeatureContext::ichDieFunktionAusfuhre()
    Dann sollte ich die Zahl "25" erhalten     # FeatureContext::sollteIchDieZahlErhalten()

1 scenario (1 passed)
4 steps (4 passed)
0m0.024s

Process finished with exit code 0 at 22:53:31.
Execution time: 268 ms.

Behat hat nun alle Schritte ausgeführt und das Ergebnis mit dem erwarteten Ergebnis vergleichen. Nachdem nun bereits einige Schritte angelegt wurden, ist es nun sehr einfach, noch weitere Szenarien zu erstellen, die diese Schritte auch verwenden und bei denen sich jeweils nur die Parameter ändern. Dazu schreiben Sie einfach weitere Szenarien im selben Format untereinander. Behat führt diese dann nacheinander aus.

Wir haben Ihnen nun gezeigt, wie einfach es ist, verhaltensorientiert mit Behat zu entwickeln und zu testen. Mit dieser Methode ist es möglich, einfache Anweisungen aufzuschreiben, die für Menschen gut lesbar sind und diese dann in Code umzusetzen. Wenn Sie genau so viel Gefallen daran gefunden haben, wie wir, scheuen Sie sich nicht, dieses Tool auch in Ihrem Projekt zu verwenden. Wir wünschen Ihnen viel Spaß dabei.

0 Kommentare

Dein Kommentar

An Diskussion beteiligen?
Hinterlasse uns Deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert