Advertisement
  1. Code
  2. Python

Verhaltensgesteuerte Entwicklung in Python

Scroll to top
Read Time: 13 min

() translation by (you can also view the original English article)

Verhaltensorientierte Entwicklung ist ein hervorragender Prozess für die Softwareentwicklung. Da das Testen häufig eine Praxis ist, die bis zur letzten Minute beiseite geschoben (oder gänzlich ignoriert) wird, kann sich das Einbinden des Prozesses in Ihren täglichen Workflow als äußerst vorteilhaft für die Qualität Ihres Codes erweisen. Die Struktur und das Design der Tests in Verbindung mit der Gherkin-Syntax machen das Lesen von Tests einfach - auch für Teammitglieder mit nicht technischem Hintergrund.

Der gesamte Code sollte gründlich getestet werden, was bedeutet, dass Fehler im Idealfall niemals die Produktion erreichen sollten. Wenn dies der Fall ist, stellt eine gründliche Testsuite, die sich auf das Verhalten Ihrer gesamten Anwendung konzentriert, sicher, dass sie leicht zu erkennen und zu beheben sind. Diese Geschwindigkeit, Klarheit, Konzentration und Qualität in Ihrem Code ist der Grund, warum Sie diesen Prozess jetzt übernehmen müssen.


Was ist verhaltensgetriebene Entwicklung?

Die verhaltensgesteuerte Entwicklung (die wir jetzt als "BDD" bezeichnen) folgt den Ideen und Prinzipien, die in der testgesteuerten Entwicklung eingeführt wurden. Die wichtigsten Punkte beim Schreiben von Tests vor dem Code gelten auch für BDD. Die Idee ist, nicht nur Ihren Code mit Komponententests auf granularer Ebene zu testen, sondern auch Ihre Anwendung mithilfe von Abnahmetests durchgängig zu testen. Wir werden diesen Teststil unter Verwendung des Lettuce-Test-Frameworks einführen.

Behavior-Driven Development (BDD) ist eine Teilmenge der Test-Driven Development (TDD).

Behavior-Driven DevelopmentBehavior-Driven DevelopmentBehavior-Driven Development

Der Prozess kann einfach definiert werden als:

  • Schreiben Sie einen fehlgeschlagenen Abnahmetest
  • Schreiben Sie einen fehlgeschlagenen Komponententest
  • Machen Sie den Unit-Test bestanden
  • Refactor
  • Machen Sie den Abnahmetest bestanden

Spülen und wiederholen Sie diese Funktion nach Bedarf.


BDD in der agilen Entwicklung

BDD kommt bei der agilen Entwicklung besonders gut zur Geltung.

Tipp: Weitere Informationen zu agilen Entwicklungsmethoden finden Sie in den Prinzipien der agilen Entwicklung.

Je nach Team müssen alle ein, zwei oder vier Wochen neue Funktionen und Anforderungen verfügbar sein. Sie müssen in der Lage sein, Code für diese Anforderungen schnell zu testen und zu schreiben. Durch Akzeptanz und Unit-Tests in Python können Sie diese Ziele erreichen.

Akzeptanztests verwenden bekanntermaßen eine "Funktion"-Datei im englischen (oder möglicherweise alternativen) Sprachformat, in der beschrieben wird, was der Test abdeckt, und die einzelnen Tests selbst. Dies kann jeden in Ihrem Team einbeziehen - nicht nur die Entwickler, sondern auch Management- und Geschäftsanalysten, die sonst keine Rolle im Testprozess spielen würden. Dies kann dazu beitragen, das Vertrauen des gesamten Teams in das, was es erreichen möchte, zu stärken.

Die Funktion-Dateien ermöglichen die Beschreibung von Tests in einer Sprache, auf die alle Unternehmensebenen zugreifen können/können, und stellen sicher, dass die bereitgestellten Funktionen so artikuliert und getestet werden, wie es das Unternehmen benötigt und erwartet. Unit-Tests allein können nicht sicherstellen, dass die gelieferte Anwendung tatsächlich die erforderliche Funktionalität bietet. Daher schaffen Abnahmetests eine weitere Vertrauensschicht in Ihren Code, um sicherzustellen, dass diese einzelnen "Einheiten" zusammenpassen, um das erforderliche Gesamtpaket zu liefern. Das Tolle an Abnahmetests ist, dass sie auf jedes Projekt angewendet werden können, an dem Sie arbeiten, egal ob groß oder klein.


Gherkin Syntax

Akzeptanztests verwenden normalerweise die Gurkensyntax, die vom Cucumber Framework für Ruby eingeführt wurde. Die Syntax ist recht einfach zu verstehen und verwendet im Lettuce Python-Paket die folgenden acht Schlüsselwörter, um Ihre Funktionen und Tests zu definieren:

  • Gegeben
  • Wann
  • Dann
  • Und
  • Funktion:
  • Hintergrund:
  • Szenario:
  • Szenarioübersicht:

Im Folgenden können Sie diese Schlüsselwörter in Aktion überprüfen und erläutern, wie sie zur Strukturierung Ihrer Abnahmetests verwendet werden können.

Installation

Die Installation des Lettuce-Pakets ist unkompliziert und folgt dem üblichen pip-install-Muster, mit dem die meisten Python-Entwickler vertraut sind.

Führen Sie die folgenden Schritte aus, um mit der Verwendung von Lettuce zu beginnen:

  • $ pip installieren lettuce
  • $ lettuce /path/to/example.feature, um Ihre Tests auszuführen. Sie können entweder nur eine Funktion-Datei ausführen oder, wenn Sie ein Verzeichnis mit Funktion-Dateien übergeben, alle ausführen.

Sie sollten auch nosetests installieren (falls Sie diese noch nicht installiert haben), da Sie einige der Aussagen von nosetests verwenden, um das Schreiben und Verwenden Ihrer Tests zu vereinfachen.

  • $ pip installieren nose

Funktion-Dateien

Funktion-Dateien sind in einfachem Englisch geschrieben und geben den Bereich der Anwendung an, den die Tests abdecken. Sie bieten auch einige Setup-Aufgaben für die Tests. Dies bedeutet, dass Sie nicht nur Ihre Tests schreiben, sondern sich auch dazu zwingen, eine gute Dokumentation für alle Aspekte Ihrer Anwendung zu schreiben. So können Sie klar definieren, was jeder Code tut und wie er behandelt wird. Dieser Dokumentationsaspekt der Tests kann mit zunehmender Größe Ihrer Anwendung sehr umfangreich sein. Sie möchten überprüfen, wie ein bestimmter Aspekt der Anwendung funktioniert, oder Sie möchten sich beispielsweise daran erinnern, wie Sie mit einem Teil der API interagieren.

Lassen Sie uns eine Funktion-Datei erstellen, die eine Anwendung testet, die für meinen Artikel Test-Driven Development in Python für Tuts+ geschrieben wurde. Die Anwendung ist nur ein einfacher Taschenrechner, der in Python geschrieben wurde, zeigt uns jedoch die Grundlagen des Schreibens von Abnahmetests. Sie sollten Ihre Anwendung mit einer app und einem tests-Ordner strukturieren. Fügen Sie im tests-ordner auch einen features-Ordner hinzu. Platzieren Sie den folgenden Code in einer Datei mit dem Namen calculator.py unter dem app-Ordner.

1
class Calculator(object):
2
    def add(self, x, y):
3
        number_types = (int, long, float, complex)
4
5
        if isinstance(x, number_types) and isinstance(y, number_types):
6
            return x + y
7
        else:
8
            raise ValueError

Fügen Sie nun den folgenden Code zu einer Datei mit dem Namen calculator.feature im Ordner tests/features hinzu.

1
Feature: As a writer for NetTuts
2
  I wish to demonstrate
3
  How easy writing Acceptance Tests
4
  In Python really is.
5
6
  Background:
7
    Given I am using the calculator
8
9
  Scenario: Calculate 2 plus 2 on our calculator
10
    Given I input "2" add "2"
11
    Then I should see "4"

Anhand dieses einfachen Beispiels können Sie sehen, wie einfach es ist, Ihre Tests zu beschreiben und sie an die verschiedenen Personen in Ihrem Team weiterzugeben.

Die Funktion-Datei enthält drei wichtige Bereiche:

  • Funktionsblock: Hier schreiben Sie eine Dokumentation darüber, was diese Testgruppe abdecken wird. Hier wird kein Code ausgeführt, aber der Leser kann genau verstehen, was diese Funktion testet.
  • Hintergrundblock: Wird vor jedem Szenario in der Funktion-Datei ausgeführt. Dies ähnelt der SetUp()-Methode und ermöglicht es Ihnen, den erforderlichen Setup-Code auszuführen, z. B. sicherzustellen, dass Sie sich auf einer Seite befinden oder bestimmte Bedingungen erfüllt sind.
  • Szenarioblock: Hier definieren Sie den Test. Die erste Zeile dient wieder als Dokumentation, und dann wechseln Sie in Ihr Szenario, um den Test auszuführen. Es sollte ziemlich leicht zu erkennen sein, wie Sie einen Test in diesem Stil schreiben können.

Schritte Datei

In Anlehnung an die Funktion-Datei muss die Schrittdatei darunter liegen. Hier geschieht die 'Magie'. Offensichtlich wird die Funktion-Datei selbst nichts tun; Es erfordert die Schritte, um tatsächlich jede Zeile zuzuordnen, um den darunter liegenden Python-Code auszuführen. Dies wird durch die Verwendung regulärer Ausdrücke erreicht.

"Reguläre Ausdrücke? Zu komplex, um sich mit Tests zu beschäftigen "kann oft eine Reaktion auf RegEx in diesen Tests sein. In der BDD-Welt werden sie jedoch verwendet, um die gesamte Zeichenfolge zu erfassen, oder verwenden sehr einfache RegEx, um Variablen aus einer Zeile auszuwählen. Daher sollten Sie sich hier nicht von ihrer Verwendung abschrecken lassen.

Reguläre Ausdrücke? Zu komplex, um sich beim Testen damit zu beschäftigen? Nicht in Salat. Simpel und einfach!

Wenn wir ein Beispiel überprüfen. Sie werden sehen, wie einfach die Steps-Datei aus der Funktion folgt.

1
from lettuce import *
2
from nose.tools import assert_equals
3
from app.calculator import Calculator
4
5
6
@step(u'I am using the calculator')
7
def select_calc(step):
8
    print ('Attempting to use calculator...')
9
    world.calc = Calculator()
10
11
12
@step(u'I input "([^"]*)" add "([^"]*)"')
13
def given_i_input_group1_add_group1(step, x, y):
14
    world.result = world.calc.add(int(x), int(y))
15
16
17
@step(u'I should see "([^"]+)"')
18
def result(step, expected_result):
19
    actual_result = world.result
20
    assert_equals(int(expected_result), actual_result)

Das erste, was es zu beachten gilt, sind die Standardimporte oben in der Datei. Wir brauchen also Zugriff auf unsere Calculator-Klasse und natürlich auf die von Lettuce bereitgestellten Werkzeugs. Sie importieren auch einige nützliche Methoden aus dem nosetest-Paket, z. B. assert_equals, um einfache Zusicherungen in den Schritten zu ermöglichen. Anschließend können Sie die Schritte für jede Zeile in der Funktion-Datei definieren. Wir können sehen, dass, wie bereits erläutert, die regulären Ausdrücke meist nur die gesamte Zeichenfolge erfassen, es sei denn, wir möchten auf die Variable innerhalb der Zeile zugreifen.

Wenn wir als Beispiel die Zeile @step(u'I input "([^"]*) "add" ([^"]*)"') verwenden, können Sie sehen, dass die Zeile zuerst mit dem @step Dekorateur aufgenommen wird . Dann verwenden Sie das Zeichen 'u' am Anfang, um eine Unicode-Zeichenfolge anzugeben, für die Lettuce reguläre Ausdrücke ausführen soll. Danach ist es nur die Zeile selbst und ein sehr einfacher regulärer Ausdruck, der mit allen Anführungszeichen übereinstimmt - den Zahlen, die in diesem Fall hinzugefügt werden müssen.

Sie sollten dann sehen, dass die Python-Methode direkt danach folgt und die Variablen mit einem beliebigen Namen an die Methode übergeben werden. Hier habe ich sie x und y genannt, um die beiden Zahlen anzugeben, die an die Rechner-add-Methode übergeben werden sollen.

Ein weiterer wichtiger Punkt ist die Verwendung der world-Variablen. Dies ist ein Container mit globalem Gültigkeitsbereich, mit dem Variablen schrittweise in einem Szenario verwendet werden können. Wenn dies nicht der Fall wäre, wären alle Variablen lokal für ihre Methode, aber hier erstellen wir einmal eine Instanz von Calculator() und greifen dann in jedem Schritt darauf zu. Sie verwenden dieselbe Technik auch, um das Ergebnis der add-Methode in einem Schritt zu speichern und das Ergebnis dann in einem anderen Schritt zu bestätigen.

Ausführen der Funktionen

Mit der Funktion-Datei und den vorhandenen Schritten können Sie jetzt die Tests ausführen und prüfen, ob sie bestanden wurden. Wie bereits erwähnt, ist die Ausführung der Tests einfach und Lettuce bietet einen integrierten Testläufer, der Ihnen nach der Installation über die Befehlszeile zur Verfügung steht. Versuchen Sie, den lettuce test/features/calculator.feature in Ihrer bevorzugten Befehlszeilenanwendung auszuführen.

1
$ lettuce tests/features/calculator.feature 
2
3
Feature: As a writer for NetTuts                 # tests/features/calculator.feature:1

4
  I wish to demonstrate                          # tests/features/calculator.feature:2

5
  How easy writing Acceptance Tests              # tests/features/calculator.feature:3

6
  In Python really is.                           # tests/features/calculator.feature:4

7
8
  Background:
9
    Given I am using the calculator              # tests/features/steps.py:6

10
    Given I am using the calculator              # tests/features/steps.py:6

11
12
  Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9

13
    Given I input "2" add "2"                    # tests/features/steps.py:11

14
    Then I should see "4"                        # tests/features/steps.py:16

15
16
1 feature (1 passed)
17
1 scenario (1 passed)
18
2 steps (2 passed)

Die Ausgabe von Lettuce ist wirklich gut, da sie Ihnen jede Zeile der ausgeführten Funktion-Datei zeigt und grün hervorhebt, um anzuzeigen, dass sie die Zeile erfolgreich bestanden hat. Außerdem wird angezeigt, welche Funktion-Datei ausgeführt wird und welche Zeilennummer nützlich ist, wenn Sie eine größere Testsuite mit zahlreichen Funktionen erstellt haben und eine fehlerhafte Zeile einer Funktion suchen müssen, z. B. wenn ein Test fehlschlägt. Schließlich erhalten Sie im letzten Teil der Ausgabe Statistiken über die Anzahl der ausgeführten Funktionen, Szenarien und Schritte sowie über die Anzahl der bestandenen Funktionen. In unserem Beispiel waren alle Tests gut, aber schauen wir uns an, wie Lettuce Ihnen Testfehler zeigt und wie Sie sie debuggen und beheben können.

Nehmen Sie eine Änderung am Code von calculator.py vor, damit der Test fehlschlägt. Ändern Sie beispielsweise die Add-Methode, um die beiden übergebenen Zahlen tatsächlich zu subtrahieren.

1
class Calculator(object):
2
    def add(self, x, y):
3
        number_types = (int, long, float, complex)
4
5
        if isinstance(x, number_types) and isinstance(y, number_types):
6
            return x - y
7
        else:
8
            raise ValueError

Wenn Sie nun die Funktion-Datei mit Lettuce ausführen, sehen Sie, wie deutlich wird, was im Test schief gelaufen ist und welcher Teil des Codes fehlgeschlagen ist.

1
$ lettuce tests/features/calculator.feature 
2
3
Feature: As a writer for NetTuts                 # tests/features/calculator.feature:1

4
  I wish to demonstrate                          # tests/features/calculator.feature:2

5
  How easy writing Acceptance Tests              # tests/features/calculator.feature:3

6
  In Python really is.                           # tests/features/calculator.feature:4

7
8
  Background:
9
    Given I am using the calculator              # tests/features/steps.py:6

10
    Given I am using the calculator              # tests/features/steps.py:6

11
12
  Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9

13
    Given I input "2" add "2"                    # tests/features/steps.py:11

14
    Then I should see "4"                        # tests/features/steps.py:16

15
    Traceback (most recent call last):
16
      File "/Users/user/.virtualenvs/bdd-in-python/lib/python2.7/site-packages/lettuce/core.py", line 144, in __call__
17
        ret = self.function(self.step, *args, **kw)
18
      File "/Users/user/Documents/Articles - NetTuts/BDD_in_Python/tests/features/steps.py", line 18, in result
19
        assert_equals(int(expected_result), actual_result)
20
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 515, in assertEqual
21
        assertion_func(first, second, msg=msg)
22
      File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 508, in _baseAssertEqual
23
        raise self.failureException(msg)
24
    AssertionError: 4 != 0
25
26
1 feature (0 passed)
27
1 scenario (0 passed)
28
2 steps (1 failed, 1 passed)
29
30
List of failed scenarios:
31
  Scenario: Calculate 2 plus 2 on our calculator # tests/features/calculator.feature:9

32

Der erwartete Wert von 4 stimmt jetzt eindeutig nicht mehr mit dem tatsächlichen Rückgabewert von 0 überein. Salat hat Ihnen dieses Problem deutlich gezeigt, und Sie können dann Ihren Code debuggen, um herauszufinden, was schief gelaufen ist, eine Korrektur anwenden und den Test erneut bestehen .


Alternative Werkzeugs

CucumberCucumberCucumber

In Python gibt es viele alternative Optionen für diese Testform. Wir haben Beispiele wie Behave, Lettuce und auch Cucumber, die, wie erwähnt, diese Struktur definiert haben. Die anderen Werkzeugs sind im Wesentlichen Klone/Ports von Cucumber. Gurke kann mit Python-Code über einen Ruby-Python-Interpreter verwendet werden, was jedoch den Rahmen dieses Tutorials sprengt.

  • Benehmen Sie sich: eine nahezu exakte Portion Gurke in Python. Hat eine gute Dokumentation und wird von den Entwicklern ständig aktualisiert. Sie bieten auch einen Vergleich mit anderen Werkzeuge, der eine Lektüre wert ist.
  • Freshen: Ein weiterer direkter Port von Cucumber mit Tutorials und Beispielen auf der Website sowie einfachen Installationswerkzeugen wie 'pip'.

Der entscheidende Punkt bei all diesen Werkzeugs ist, dass sie alle mehr oder weniger gleich sind. Sobald Sie eines gemeistert haben, werden Sie schnell die anderen erkennen, falls Sie sich für einen Wechsel entscheiden. Eine schnelle Überprüfung der Dokumentation sollte für die meisten Entwickler, die Python beherrschen, ausreichen.


Vorteile

Sie können sicher in das Refactoring eintauchen.

Die Verwendung einer gründlichen Testsuite bietet erhebliche Vorteile. Eine der wichtigsten dreht sich um das Refactoring von Code. Mit einer robusten Testsuite können Sie sicher in das Refactoring eintauchen, da Sie wissen, dass Sie kein vorheriges Verhalten in Ihrer Anwendung verletzt haben.

Dies gewinnt an Bedeutung, je mehr sich Ihre Anwendung entwickelt und größer wird. Wenn Sie mehr und mehr Legacy-Code haben, wird es sehr schwierig, zurück zu gehen und Änderungen mit Zuversicht vorzunehmen und zu wissen, dass Sie definitiv kein bestehendes Verhalten gebrochen haben. Wenn Sie für jede zu entwickelnde Funktion eine vollständige Reihe von Abnahmetests geschrieben haben, wissen Sie, dass Sie diese vorhandene Funktionalität nicht verletzt haben, solange Sie bei Ihren Änderungen einen vollständigen Build Ihrer Tests ausführen, bevor Sie die Änderungen live übertragen. Sie überprüfen, ob Ihr Code aufgrund Ihrer Änderungen und Einschränkungen nicht "zurückgegangen" ist.

Ein weiterer großer Vorteil des Einbaus von Abnahmetests in Ihren täglichen Workflow ist die Möglichkeit, vor Beginn der Entwicklung einer Funktion eine Klärungssitzung abzuhalten.

Sie können beispielsweise die Entwickler, die die Lösung eines Features codieren, die Tester (Qualitätssicherung/Qualitätssicherung), die den Code nach Abschluss testen, und den geschäftlichen/technischen Analysten zusammensetzen und die Anforderungen einer Funktion klären. und dokumentieren Sie dies dann als die Funktion-Dateien, auf die die Entwickler hinarbeiten werden.

Im Wesentlichen können Sie eine Reihe von fehlerhaften Funktion-Dateien haben, die die Entwickler ausführen und einzeln übergeben können, damit sie wissen, dass sie mit der Funktion fertig sind, sobald alle übergeben wurden. Dies gibt Entwicklern den Fokus, den sie benötigen, um genau die Anforderungen zu erfüllen und den Code nicht mit Funktionen und Funktionalität zu erweitern, die nicht unbedingt erforderlich sind (auch als "Vergoldung" bezeichnet). Die Tester können dann die Funktionsdateien überprüfen, um festzustellen, ob alles angemessen abgedeckt ist. Der Prozess kann dann für die nächste Funktion durchgeführt werden.


Abschließende Gedanken

Nachdem ich in einem Team mit den oben beschriebenen Prozessen und Werkzeugs gearbeitet habe, habe ich persönlich die enormen Vorteile dieser Arbeitsweise erlebt. BDD bietet Ihrem Team Klarheit, Konzentration und das Vertrauen, großartigen Code zu liefern und gleichzeitig mögliche Fehler auf ein Minimum zu beschränken.

Kopf hoch!

Wenn dieser Artikel Ihren Appetit auf die Welt des Testens in Python geweckt hat, lesen Sie doch mein Buch "Testing Python", das kürzlich bei Amazon und anderen guten Einzelhändlern veröffentlicht wurde. Besuchen Sie diese Seite, um noch heute Ihr Exemplar des Buches zu kaufen und einen Ihrer Tuts+-Beitragenden zu unterstützen.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.