Blog

Akzeptanztests mit Test::BDD::Cucumber

01.10.2020 // Gregor Goldbach

Akzeptanztests sind Tests, die als natürlichsprachliche Szenarien aus Anwendersicht formuliert werden. Sie dienen als lebendige Dokumentation eines Systems und können das gemeinsame Verständnis im Team herstellen. Test::BDD::Cucumber ist eine Distribution, mit der Fehler in Perl-Anwendungen durch die Ausführung von Akzeptanztests gefunden werden können.

Klassische Perl-Tests: von innen nach außen

In Perl sind Unit-Tests weit verbreitet. Oftmals sind diese als einzige Testart nach außen sichtbar, weil sie in CPAN-Distributionen ausgeliefert werden. Mit der CPAN Testers Matrix gibt es eine transparente Möglichkeit, die Ergebnisse der Unit-Tests einzusehen.

Integrations- oder Systemtests sind bei weitem nicht so bekannt in der Perl-Welt, weil es hierfür weder ein einheitliches Framework noch eine ähnlich transparente Plattform gibt.

Unit-Tests arbeiten »von innen nach außen« (»inside-out«): Die Units bilden die untersten Bausteine, aus denen Anwendungen erstellt werden. Unit-Tests sind gut geeignet für die testgetriebende Entwicklung von Units und üblicherweise schnell in der Ausführung. Deswegen ist es praktisch möglich und ratsam, eine hohe Testabdeckung durch eine Vielzahl von Testfällen zu erreichen.

Akzeptanztests: von außen nach innen

In der Softwaretechnik gibt es die Disziplin des »behaviour driven development« (BDD), in der Akzeptanztests ein Bestandteil sind.

Diese Art von Systemtests heißt so, weil mit ihnen die Bedingungen geprüft werden, unter denen ein Anwender die Umsetzung der Software akzeptiert. Sie sind natürlichsprachlich aus Sicht des Anwenders formuliert.

Da Anwender in Funktionalitäten denken, werden Akzeptanztests pro Feature geschrieben. Sie bestehen aus Szenarien, die exemplarisch die Verwendung der Software im Rahmen eines Features beschreiben.

Es wird das vollständig integrierte System »von außen nach innen« (»outside-in«) getestet.

Da das vollintegrierte System getestet wird, sind diese Test langsam. Es werden daher nur einzelne bespielhafte Fälle – die Szenarien – getestet.

Akzeptanztests passen gut zu testgetriebener Entwicklung einer Anwendung. In dieser Vorgehensweise spezifiziert man erst das Verhalten der Software aus Sicht des Anwenders in Szenarien und implementiert anschließend das Feature. Die Szenarien helfen beim Konzentrieren auf das Wesentliche eines Features.

Beispiel

Wie sieht nun ein solcher Akzeptanztest aus? Das folgende Beispiel zeigt ein Szenario eines erfolgreichen Logins.

Feature: Login
  Scenario: User logs in providing correct credentials
    Given I am an existing user
    When I log in providing the correct password
    Then the login succeeds
    And I am at the dashboard page

Szenarien werden nach dem Schema »Arrange, Act, Assert« (AAA) mit der Given-When-Then-Schablone geschrieben. Das Szenario beschreibt aus der Sicht der Benutzers die wesentliche Funktionalität: Nach einem erfolgreichen Login wird der Benutzer auf sein Dashboard geleitet.

Der »Given«-Schritt entspricht »Arrange«, in dem die Voraussetzungen für den Test geschaffen werden. In diesem Fall wird das getestete System so verändert, dass ein Benutzer existiert.

Der »When«-Schritt entspricht »Act«. Hier wird die zu testende Funktionalität ausgeführt.

Im abschließenden »Then«-Schritt (»Assert«) werden die erwarteten Ergebnisse geprüft.

Technische Umsetzung mit Test::BDD::Cucumber

Die Ausführung solcher Szenarien kann in Perl mit einer Reihe von Distributionen erfolgen. Die ausgereifteste ist Test::BDD::Cucumber. Die Akzeptanztests werden standardmäßig im Verzeichnis t/features abgelegt. Szenarien eines Features werden in einer Datei beschrieben, die auf .feature endet.

Die einzelnen Schritte werden als Zeichenketten mit regulären Ausdrücken geprüft. Welche dies sind, legen die Entwickler über Subroutinenaufrufe fest. Diese werden in allen Dateien im Verzeichnis t/features/step_definitions erwartet.

Für unser oben gezeigtes Szenario könnte die Deklaration der einzelnen Schritte wie folgt aussehen:

Given qr{there is an existing user} => sub {
    my $context = shift;
    
    # Benutzer anlegen
    ...
};

When qr{I log in providing the correct password} => sub {
    my $context = shift;
    
    # Anmeldung mit korrektem Passwort durchführen
    ...
};

Then qr{the login succeeds} => sub {
    my $context = shift;
    
    # prüfen, ob Login erfolgreich war
    ...
};

Then qr{I am at the dashboard page} => sub {
    my $context = shift;
    
    # prüfen, ob Dashboard angezeigt wird
    ...
};

Jeder Schritt wird durch diese regulären Ausdrücke geprüft. Bei einer Übereinstimmung wird der entsprechende Code ausgeführt.

Wie im Beispiel zu sehen ist, wird an die Subroutine der Kontext übergeben, in dem der Code ausgeführt wird. Über diesen Kontext können Informationen zwischen den Schritten ausgetauscht werden, ohne auf globale Zustände zugreifen zu müssen. In unserem Beispiel würde hier zum Beispiel der angelegte Benutzer übergeben werden, so dass Name und Passwort nicht fest im Code verdrahtet werden müssen.

Der jeweilige Then-Schritt prüft den erwarteten Zustand mit den aus Test::More bekannten Werkzeugen.

Die Akzeptanztests werden mit dem Werkzeug pherkin in der Shell aufgerufen. Bei der Ausführung eines Test werden alle Schritte ausgegeben. Fehler werden laut und deutlich in der Konsole gemeldet:

$ pherkin -Ilib t/features/login.feature 
  Feature: Login
    Scenario: User logs in providing correct credentials
      Given I am an existing user
      When I log in providing the correct password
      Then the login succeeds
      step defined at t/features/login.feature line N.
              ok - Starting to execute step: the login succeeds
              not ok 1
              #   Failed test at ...

In den »Then«-Schritten können alle Test::Builder-basierten Testwerkzeuge verwendet werden, die die Entwickler auch sonst in ihren Unit-Tests einsetzen.

Die obigen Beispiele können nur skizzenhaft das Vorgehen zeigen. Die Test::BDD::Cucumber-Distribution enthält einige lauffähige Beispiele, die konkret die Funktionalität der Distribution zeigen.

Vorteile von Akzeptanztests

Durch das Verwenden von Akzeptanztests ist das Team gezwungen, sich aus der Sicht der Benutzer der Implementierung zu nähern. Dadurch wird häufig deutlich, dass das Wesentliche der Umsetzung einer Software in der Regel nicht in den grafischen Details liegt, sondern vielmehr in der Funktionalität, die der Benutzer erwartet.

Das Team formuliert die Szenarien in einer allgemeinen Sprache (»ubiquitäre Sprache«) der Anwendungsdomäne. Da dieses bereits bekannte Vokabular verwendet wird, kann das gemeinsame Verständnis über die Funktionsweise der Software erhöht werden. Da die Tests zudem ausführbar sind, erhält das Team so eine ausführbare, lebendige Dokumentation des tatsächliche funktionierenden Systems.

Akzeptanztests testen die lauffähige Software mit all ihren Komponenten und nicht nur einzelne Teile. Sie sind daher sehr gut dazu geeignet, um nach Abschluss von Unit- und Integrationstests Fehler in der Zusammenarbeit aller Softwarebausteine einer Anwendung zu finden.

Abschließend führe ich noch einige bewährte Vorgehensweisen auf, um diese Vorteile tatsächlich nutzen zu können.

Best Practices

  • Szenarien sollen allgemein formuliert werden und keine Details erwähnen, da die Tests sonst sehr brüchig sind.
  • Szenarien sollen nur exemplarisch formuliert werden, da Akzeptanztests langsam sind. Es sollen nicht alle Testfälle abgedeckt werden; hierfür sind Unit-Tests besser geeignet.
  • Die Szenarien sollen in dem im Team verwendeten Vokabular formuliert werden, um darüber das gemeinsame Verständnis zu erhöhen.
  • Das Team sollte möglichst früh mit Akzeptanztests beginnen, da darüber schnell ein Prototyp der Anwendung erstellt werden kann und so testbarer Code entsteht.
  • Szenarien sollen aus Benutzersicht geschrieben werden. Darin wird die Software wie durch einen Benutzer bedient:
    • Wenn die getestete Software ein REST-Server, wird ein REST-Client verwendet.
    • Wenn die getestete Software eine Webanwendung, wird ein Browser ferngesteuert.
    • Wenn die getestete Software ein Kommandozeilenwerkzeug, dann wird dieses wirklich aufgerufen und STDOUT und STDERR ausgewertet.
  • Es sollen keine Komponente gemockt werden, sondern die Anwendung soll in einer Testumgebung laufen.

Zusammenfassung

Das Testen mit Akzeptanztests erfordert im Team einiges Umdenken im Vergleich zum Testen mit Unit-Tests. Es wird aber damit belohnt, dass im Ergebnis eine lebendige und ausführbare Dokumentation des Systems vorliegt. Diese Dokumentation wurde mit einem gemeinsamen Vokabular verfasst, das das gemeinsame Verständnis ermöglicht.

Test::BDD::Cucumber ist eine Distribution, mit der Akzeptanztests in Perl geschrieben werden können. Die Distribution kann verwendet werden, um nach der erfolgreichen Ausführung von Unit-Tests Fehler im voll integrierten System zu finden.

Weiterlesen


Permalink: