TDD

Testgetriebene Entwicklung in Laravel

Beim Erstellen einer Anwendung oder neuer Funktionalitäten können Tests während der Entwicklung parallel geschrieben werden oder erst ganz am Ende. Im Falle von TDD ist das Gegenteil der Fall. Zuerst schreiben wir einen Test für nicht vorhandene Funktionalität und dann schreiben wir einen Code, der unseren Test bestehen lässt.

So sieht der TDD-Zyklus auf einen Blick aus:

  1. Wir schreiben einen Test für nicht vorhandene Funktionalität.
  2. Wir führen den Test aus, der natürlich mit einem Fehler endet. Basierend auf dem generierten Fehler erstellen wir den minimalen Code, der es Ihnen ermöglicht, den Test zu bestehen.
  3. Nach dem Bestehen des Tests entwickeln wir unseren Code weiter und führen die Tests erneut aus.

Wir können diesen Zyklus auch rot, grün, refaktorieren nennen. Der erste Testlauf erzeugt Fehler (rot), nach dem Schreiben des verantwortlichen Codes für die neue Funktionalität endet das erneute Ausführen des Tests mit einem positiven Ergebnis (grün). Dann entwickeln wir unseren Code weiter (refaktorieren).

Für die Zwecke dieses Artikels nehmen wir an, dass wir eine Anwendung entwickeln werden, die die Möglichkeit bietet, Beiträge zu bearbeiten und zu löschen.

Vorbereitung

Im Falle von Laravel sind viele Dinge bereits vorbereitet und das Erstellen eines neuen Tests ist äußerst einfach. Um loszulegen, müssen wir mit der Datenbankkonfiguration beginnen. In der phpunit.xml-Datei, die sich im Hauptverzeichnis des Projekts zwischen den Tags befindet, müssen wir folgenden Code hinzufügen (oder im Falle von Laravel 8 auskommentieren):

<server name="DB_CONNECTION" value="sqlite"/>

<server name="DB_DATABASE" value=":memory:"/> 

Dank dessen müssen wir beim Ausführen des Tests nicht die Datenbank verwenden, die wir jeden Tag beim Erstellen der Anwendung verwenden. Dann müssen wir die entsprechende Datei erstellen, in der wir die Tests schreiben werden. Wie immer kommt bei Laravel Artisan zur Hilfe:

php artisan make:test PostTest

Ein neues Resource erstellen

In der neu erstellten Datei tests/Feature/PostTest.php erstellen Sie einen neuen Test, um neue Ressourcen einzurichten.

  /** @test */

    public function a_post_can_be_created()
    {
        $response = $this->post('/posts', [
            'title' => 'Dies ist ein Testbeitrag.',
            'body' => 'Ein wenig Lorem Ipsum Text.'
        ]);

        $response->assertOk();
        $this->assertCount(1, Post::all());
    }

Beachten Sie einige wichtige Punkte:

  1. Ein Kommentar, der @test enthält. Das ist notwendig, um unseren Test zu starten.
  2. Der Name der Methode ist besser, wenn er lang ist, aber er sollte die im Test durchgeführten Aktivitäten korrekt beschreiben, anstatt kurz und nichtssagend zu sein.

Lassen Sie uns durchgehen, was unser Test umfasst. Nach dem Senden einer POST-Anfrage an /posts mit Titel- und Body-Daten erwarten wir eine Serverantwort mit einem Status von 200 und dass nach dem Herunterladen aller Posts aus der Datenbank diese gleich eins sein werden.

Leider wird beim erneuten Ausführen des Tests die Anzahl der Posts in der Datenbank 2 betragen (Post aus dem vorherigen Test und der aktuelle). Wir müssen also die Datenbank jedes Mal leeren, wenn wir den Test beenden. Beachten Sie, dass wir das entsprechende Trait bereits oben in der Datei importiert haben:

use Illuminate\Foundation\Testing\RefreshDatabase;

Wir müssen es nur in unserer Klasse verwenden:

use RefreshDatabase;

Versuchen wir also, unseren ersten Test auszuführen. Das tun wir mit dem Befehl:

./vendor/bin/phpunit --filter a_post_can_be_created

Natürlich wird unser Test fehlschlagen, weil wir keine fertige Routenführung, keinen Controller oder kein Modell haben. Also lassen Sie uns alle notwendigen Dinge vorbereiten und versuchen Sie, unseren Test erneut auszuführen, indem wir den Controller, das Modell und die Routenführung erstellen.

Fehlerbehandlung

Manchmal wird beim Ausführen der Tests ein Fehler zurückgegeben. Sein Inhalt wird nicht sehr hilfreich sein und auf den ersten Blick wird es schwer zu verstehen sein, was die Ursache ist:

1) Tests\Feature\PostTest::a_post_can_be_created
Response status code [500] does not match expected 200 status code.

Um detailliertere Informationen zum Fehler zu erhalten, müssen wir die Fehlerbehandlung von Laravel deaktivieren. Fügen wir also diesen Code hinzu:

$this->withoutExceptionHandling();

Jetzt, nach dem Neustart des Tests, erhalten wir Informationen, die für uns viel nützlicher sein werden:

1) Tests\Feature\PostTest::a_post_can_be_created
Illuminate\Database\Eloquent\MassAssignmentException: Add [title] to fillable property to allow mass assignment on [App\Models\Post]

Ändern einer vorhandenen Ressource

Nun erstellen wir einen Test, um die Möglichkeit zu überprüfen, einen bereits erstellten Beitrag zu ändern. Wir fügen eine neue Methode hinzu:

    /** @test */

    public function a_post_can_be_updated()
    {

    }

Bei jedem Test starten wir mit einer sauberen Datenbank, die keine Daten enthält. Der Test muss also mit der Erstellung einer neuen Ressource beginnen und dann versuchen, diese zu ändern.

/** @test */

public function a_post_can_be_updated()
{
    $this->post('/posts', [
        'title' => 'Beitragstitel',
        'body' => 'Beitragsinhalt',
    ]);

    $this->assertCount(1, Post::all());
    $post = Post::first();

    $this->patch('/posts/' . $post->id, [
        'title' => 'Neuer Titel',
        'body' => 'Neuer Inhalt',
    ]);

}

Wir müssen überprüfen, ob sich Titel und Inhalt geändert haben. Dazu müssen wir nur den ersten (und einzigen) Beitrag herunterladen und seinen Inhalt vergleichen:

$this->assertEquals('Neuer Titel', Post::first()->title);
$this->assertEquals('Neuer Inhalt', Post::first()->body);

Es ist an der Zeit, den Test auszuführen und zu sehen, was das Ergebnis sein wird:

./vendor/bin/phpunit --filter a_post_can_be_updated

Wieder ist die Antwort, die zurückgegeben wird, nicht sehr hilfreich:

1) Tests\Feature\PostTest::a_post_can_be_updated
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Neuer Titel'
+'Beitragstitel’

Jetzt wissen wir jedoch, was in diesem Fall zu tun ist. Schalten wir also die Fehlerbehandlung ab und führen den Test erneut aus. Dieses Mal sollten wir Informationen über das Fehlen der richtigen Routenführung erhalten. Gemäß der TDD-Methodologie sollten wir eine Routenführung und eine Methode im Controller erstellen, um die Änderungen am Beitrag zu speichern. Wenn wir alles richtig gemacht haben, sollte der Test erfolgreich sein.

Entfernen einer Ressource

Wir werden den Test ganz ähnlich wie bei der Änderung der Ressource starten. Aufgrund der Tatsache, dass keine Datensätze in unserer Datenbank vorhanden sind, müssen wir den Test damit beginnen, einen neuen Beitrag hinzuzufügen, um dessen Entfernung testen zu können. Lassen Sie uns also eine neue Methode erstellen.

 /** @test */

public function a_post_can_be_deleted()
{
    $this->withoutExceptionHandling();
    $this->post('/posts', [
        'title' => 'Beitragstitel',
        'body' => 'Beitragsinhalt',
    ]);

    $this->assertCount(1, Post::all());
}

Dann senden wir die entsprechende Anfrage und überprüfen erneut, ob die Post-Tabelle diesmal keine Datensätze enthält.

$post = Post::first();
$this->delete('/posts/' . $post->id);
$this->assertCount(0, Post::all());

Natürlich wird auch dieser Test fehlschlagen. Darum geht es bei TDD. Jetzt ist es an der Zeit, die entsprechende Routenführung und eine Methode im Controller zu erstellen, die diese Anfrage bearbeitet.

Testgruppierung

Wenn es mehr Tests gibt, lohnt es sich, diese zu gruppieren. In Zukunft müssen Sie diese nicht alle auf einmal ausführen oder jeden einzelnen manuell starten. Fügen Sie dazu @group im Kommentar über jeder Methode hinzu. Für diesen Artikel heißen die Tests: post_test_group. Um Tests aus dieser Gruppe auszuführen, geben Sie im Terminal ein:

./vendor/bin/phpunit --group post_test_group

Zusammenfassung

TDD entdeckt mögliche Fehler in der Anwendung viel schneller als im normalen Arbeitsmodus, wenn die Tests am Ende geschrieben werden. Wir sparen viel Zeit bei der möglichen Verbesserung des Codes, und weniger Personen sind am gesamten Prozess beteiligt. Es sollte jedoch beachtet werden, dass TDD bei kleinen Anwendungen nicht funktioniert und zusätzliche Fähigkeiten vom Entwicklungsteam erfordert, die von der QA-Gruppe übernommen werden.

Bei Droptica haben wir langjährige Erfahrung in der Bereitstellung von PHP-Entwicklungsdiensten. Bitte kontaktieren Sie uns, wenn Sie mehr über Testgetriebene Entwicklung erfahren möchten. Unsere Laravel-Entwickler stehen Ihnen gerne zur Verfügung.

Und wenn Sie sich für Laravel interessieren, empfehle ich, meinen Artikel zu lesen, in dem ich über die Intuitivität und Schnelligkeit beim Schreiben von Code mit Laravel spreche.

3. Best practices for software development teams