Was ist BDD-Testing? Verhaltensgetriebenes Entwicklungsframework

⚡ Intelligente Zusammenfassung

BDD-Testing beschreibt das Anwendungsverhalten in einfacher Given-When-Then-Sprache, sodass Analysten, Entwickler und Tester eine gemeinsame Spezifikation verwenden. Diese Anleitung wendet diesen Ansatz auf REST-API-Tests mit Behave an. Python Framework, das Einrichtung, Feature-Dateien, Schrittimplementierungen, Ausführung und Berichterstellung umfasst.

  • 🗣️ Kernprinzip: Behavior-Driven Development erweitert Test-Driven Development, indem es jedes Szenario in natürlicher Sprache ausdrückt, die auch Nicht-Programmierer lesen und genehmigen können.
  • 🧱 Szenariostruktur: Given legt Vorbedingungen fest, When führt die Aktion aus, und Then stellt das Ergebnis fest, während And oder But den Dekorator des vorhergehenden Schritts erben.
  • 🌐 REST-Abdeckung: Das Beispiel demonstriert das vollständige CRUD-Verhalten mittels POST, GET, PUT und DELETE gegenüber dem öffentlichen jsonplaceholder-Posts-Dienst.
  • 🐍 Framework-Einrichtung: Behave wird über pip installiert auf Python 3 und erwartet ein Verzeichnis „features“, das Feature-Dateien enthält, sowie ein Paket „steps“, das die Implementierungen enthält.
  • 🔗 Stufenbindung: Dekoratoren wie @given, @when und @then passen zum Feature-Text, und in geschweifte Klammern gesetzte Platzhalter übergeben Werte aus dem Szenario an die Funktion. Python Funktion.
  • ▶ ️ Ausführung: Der Pretty Formatter gibt für jeden Schritt ein farbcodiertes Ergebnis (bestanden/nicht bestanden) in der Konsole aus.
  • 📊 Reporting: Der Formatter allure-behave erzeugt maschinenlesbare Ergebnisse, die von der Allure-Befehlszeile als durchsuchbarer HTML-Bericht dargestellt werden.

Was ist BDD-Testing?

Was sind BDD-Tests (Behavior Driven Development)?

BDD-Tests (verhaltensgesteuerte Entwicklung). BDD ist eine Technik der agilen Softwareentwicklung und eine Erweiterung von TDD (Testgetriebene Entwicklung). Bei BDD werden Testfälle in einer natürlichen Sprache verfasst, die auch Nicht-Programmierer verstehen können.

Wie funktioniert BDD-Testen?

Angenommen, Sie sind beauftragt, ein Modul für Geldtransfers in einer Online-Banking-Anwendung zu erstellen.

Es gibt mehrere Möglichkeiten, es zu testen:

  1. Die Geldüberweisung sollte erfolgen, wenn auf dem Quellkonto ausreichend Guthaben vorhanden ist.
  2. Die Überweisung sollte erfolgen, wenn die Angaben zum Zielkonto korrekt sind.
  3. Die Geldüberweisung sollte erfolgen, wenn das vom Benutzer eingegebene Transaktionspasswort/RSA-Code/die Sicherheitsauthentifizierung korrekt ist.
  4. Geldüberweisungen sollten auch an Bankfeiertagen erfolgen.
  5. Der Geldtransfer sollte zu einem vom Kontoinhaber festgelegten Datum in der Zukunft erfolgen

Das Testszenario wird immer aufwendiger und komplexer, wenn wir zusätzliche Funktionen wie die Überweisung des Betrags X über einen Zeitraum von Y Tagen oder Monaten, Stoppen usw. berücksichtigen.ping eine planmäßige Überweisung, wenn der Gesamtbetrag Z erreicht, und so weiter.

Die allgemeine Tendenz von Entwicklern besteht darin, Funktionen zu entwickeln und den Testcode später zu schreiben. Wie das obige Beispiel zeigt, Testfall Die Entwicklung hier ist komplex, daher wird der Entwickler sie verschieben. Tests bis zur Veröffentlichung, zu deren Zeitpunkt schnelle, aber ineffektive Tests durchgeführt werden.

Um dieses Problem zu lösen, wurde die verhaltensgetriebene Entwicklung (Behavior Driven Development, BDD) entwickelt. Sie vereinfacht den gesamten Testprozess für Entwickler.

In BDD muss alles, was Sie schreiben, eingefügt werden Gegeben-Wann-Dann Schritte. Betrachten wir das obige Beispiel im BDD-Kontext:

Given that a fund transfer module in net banking application has been developed
And I am accessing it with proper authentication

When I shall transfer with enough balance in my source account
Or I shall transfer on a Bank Holiday
Or I shall transfer on a future date
And destination a/c details are correct
And transaction password / rsa code / security authentication for the transaction is correct
And press or click send button

Then amount must be transferred
And the event will be logged in log file

Dieses Formular ist leicht zu erstellen, zu lesen und zu verstehen. Es deckt alle möglichen Testfälle für das Geldtransfermodul ab und lässt sich schnell erweitern. Es liest sich wie eine lebendige Dokumentation für das Modul. Da BDD aus TDD hervorgegangen ist, ist es wichtig, die Unterschiede zwischen den beiden Ansätzen zu klären, bevor man sich mit den Werkzeugen befasst.

BDD vs TDD: Wichtigste Unterschiede

Beide Vorgehensweisen sind testorientiert und verkürzen die Feedbackschleifen. Sie unterscheiden sich darin, wer die Tests erstellt, welche Sprache diese Tests verwenden und welche Ebene der Anwendung sie beschreiben.

Aspekt TDD (Testgetriebene Entwicklung) BDD (verhaltensgesteuerte Entwicklung)
Optik Wie eine Codeeinheit intern funktioniert Wie sich das System aus Benutzersicht verhält
Sprache Zusicherungen in Programmiersprachen Natürlichsprachliche Gegeben-Wenn-Dann-Szenarien
Hauptautor Entwickler:in / Unternehmen Business-Analyst, Produktverantwortlicher, Tester und Entwickler arbeiten zusammen
Readable von Nicht-Programmierern Nein Ja
Typischer Umfang Einheitsebene Funktion, API und Akzeptanzniveau
Gängige Werkzeuge JUnit, pytest, NUnit Benehmen, CucumberSpecFlow, JBehave

Die beiden ergänzen sich eher, als dass sie miteinander konkurrieren. Teams halten üblicherweise TDD Für das Design auf Unit-Ebene werden BDD-Szenarien hinzugefügt, um das Verhalten zu beschreiben, das die Stakeholder tatsächlich freigeben. Da REST-Endpunkte genau auf dieser Akzeptanzebene angesiedelt sind, eignen sie sich hervorragend für BDD.

Was ist REST-API-Testen?

Da REST sich zu einem beliebten Stil für die Entwicklung von APIs entwickelt hat, ist die Automatisierung von REST-API-Testfällen neben UI-Testfällen gleichermaßen wichtig geworden. API-Tests beinhaltet das Testen von CRUD-Aktionen (Erstellen, Lesen, Aktualisieren, Löschen) mit den Methoden POST, GET, PUT und DELETE.

Was ist Verhalten?

Behave ist eine der beliebtesten Python BDD-Testframeworks. So funktioniert Behave:

  • Feature-Dateien werden von Ihrem Business Analysten, Sponsor oder dem Verantwortlichen für die Verhaltensszenarien erstellt. Eine Feature-Datei verwendet ein natürlichsprachliches Format, um ein Feature oder einen Teil eines Features anhand repräsentativer Beispiele für erwartete Ergebnisse zu beschreiben.
  • Diese Szenarioschritte sind Schrittimplementierungen zugeordnet, die in geschrieben wurden Python.
  • Optional können Umgebungssteuerungen Code vor und nach Schritten, Szenarien, Funktionen oder dem gesamten Durchlauf ausführen.

Nachdem die Rolle jeder einzelnen Datei geklärt ist, kann das Framework nun installiert werden.

Einrichten des BDD-Test-Frameworks. Verhalten Sie sich weiter Windows

Installation:

Projektaufbau:

  • Erstellen Sie ein neues Projekt
  • Erstellen Sie die folgende Verzeichnisstruktur

Projektaufbau

Der obige Screenshot zeigt das von Behave erwartete Layout: a Funktionen Verzeichnis, das die Feature-Dateien enthält, und ein verschachteltes Schritte Verzeichnis, das die Python Implementierungen. Behave erkennt beide anhand des Namens, daher müssen die Ordnernamen exakt übereinstimmen.

Feature-Dateien:

Nun erstellen wir die Feature-Datei. Sample_REST_API_Testing.featureDie Funktion besteht in CRUD-Operationen am Dienst „Beiträge“.

In diesem Beispiel wird die https://jsonplaceholder.typicode.com/ posts sample REST service, a free fake API that accepts write requests and returns realistic answers without persistent changes.

Beispiel-POST-Szenario

Scenario: POST post example -> creating a new post item using the 'posts' service
Given I set post posts API endpoint          -> prerequisite: sets the URL of the posts service
When I set HEADER param request content type as "application/json"
And set request body
And send POST HTTP request                    -> the actual test step
Then I receive valid HTTP response code 201
And Response body "POST" is non-empty       -> verification of the response body

In ähnlicher Weise können Sie die übrigen Szenarien wie folgt formulieren:

Projektaufbau

Sample_REST_API_Testing.feature

Feature: Test CRUD methods in Sample REST API testing framework

Background:
    Given I set sample REST API url

Scenario: POST post example
    Given I Set POST posts api endpoint
    When I Set HEADER param request content type as "application/json"
    And Set request Body
    And Send a POST HTTP request
    Then I receive valid HTTP response code 201
    And Response BODY "POST" is non-empty

Scenario: GET posts example
    Given I Set GET posts api endpoint "1"
    When I Set HEADER param request content type as "application/json"
    And Send GET HTTP request
    Then I receive valid HTTP response code 200 for "GET"
    And Response BODY "GET" is non-empty

Scenario: UPDATE posts example
    Given I Set PUT posts api endpoint for "1"
    When I Set Update request Body
    And Send PUT HTTP request
    Then I receive valid HTTP response code 200 for "PUT"
    And Response BODY "PUT" is non-empty

Scenario: DELETE posts example
    Given I Set DELETE posts api endpoint for "1"
    When I Send DELETE HTTP request
    Then I receive valid HTTP response code 200 for "DELETE"

Schritte zur Implementierung

Für die in den obigen Szenarien verwendeten Feature-Schritte können Sie nun Implementierungen schreiben in Python Dateien im Verzeichnis „steps“.

Das Behave-Framework identifiziert die Schrittfunktion, indem es Dekoratoren mit dem Prädikat der Feature-Datei abgleicht. Zum Beispiel ein Gegeben Das Prädikat in einem Feature-Datei-Szenario sucht nach einer Stufenfunktion, die Folgendes enthält: @given Dekorateur. Das gleiche Matching erfolgt für Wenn die Funktion und DannIm Fall von „Aber“ und „Und“ verwendet die Schrittfunktion denselben Dekorator wie ihr vorhergehender Schritt. Wenn beispielsweise „Und“ auf ein „Gegeben“ folgt, lautet der entsprechende Dekorator der Schrittfunktion: @given.

Beispielsweise kann der „When“-Schritt für POST wie folgt implementiert werden. Beachten Sie, wie "application/json" wird aus der Feature-Datei in den geschweiften Platzhalter übergeben – dies nennt man Parametrisierung.

# Decorator: the braced name captures a value from the feature file
@when(u'I Set HEADER param request content type as "{header_content_type}"')
def step_impl(context, header_content_type):
    # Step implementation: set the content type on the request header
    request_headers['Content-Type'] = header_content_type

In ähnlicher Weise erfolgt die Umsetzung der anderen Schritte in Schritt Python Die Datei sieht folgendermaßen aus:

Schritte zur Implementierung

sample_step_implementation.py

from behave import given, when, then, step
import requests

api_endpoints = {}
request_headers = {}
response_codes = {}
response_texts = {}
request_bodies = {}
api_url = None

@given(u'I set sample REST API url')
def step_impl(context):
    global api_url
    api_url = 'https://jsonplaceholder.typicode.com'

# START POST Scenario
@given(u'I Set POST posts api endpoint')
def step_impl(context):
    api_endpoints['POST_URL'] = api_url + '/posts'
    print('url :' + api_endpoints['POST_URL'])

@when(u'I Set HEADER param request content type as "{header_content_type}"')
def step_impl(context, header_content_type):
    request_headers['Content-Type'] = header_content_type

# "And" or "But" steps are renamed by behave to match their preceding step
@when(u'Set request Body')
def step_impl(context):
    request_bodies['POST'] = {"title": "foo", "body": "bar", "userId": "1"}

@when(u'Send POST HTTP request')
def step_impl(context):
    # send the request and save the response object
    response = requests.post(url=api_endpoints['POST_URL'],
                             json=request_bodies['POST'],
                             headers=request_headers)
    response_texts['POST'] = response.text
    print("post response :" + response.text)
    response_codes['POST'] = response.status_code

@then(u'I receive valid HTTP response code 201')
def step_impl(context):
    print('Post rep code ;' + str(response_codes['POST']))
    assert response_codes['POST'] == 201
# END POST Scenario

# START GET Scenario
@given(u'I Set GET posts api endpoint "{id}"')
def step_impl(context, id):
    api_endpoints['GET_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['GET_URL'])

@when(u'Send GET HTTP request')
def step_impl(context):
    response = requests.get(url=api_endpoints['GET_URL'], headers=request_headers)
    response_texts['GET'] = response.text
    response_codes['GET'] = response.status_code

@then(u'I receive valid HTTP response code 200 for "{request_name}"')
def step_impl(context, request_name):
    print('Get rep code for ' + request_name + ':' + str(response_codes[request_name]))
    assert response_codes[request_name] == 200

@then(u'Response BODY "{request_name}" is non-empty')
def step_impl(context, request_name):
    print('request_name: ' + request_name)
    print(response_texts)
    assert response_texts[request_name] is not None
# END GET Scenario

# START PUT/UPDATE
@given(u'I Set PUT posts api endpoint for "{id}"')
def step_impl(context, id):
    api_endpoints['PUT_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['PUT_URL'])

@when(u'I Set Update request Body')
def step_impl(context):
    request_bodies['PUT'] = {"title": "foo", "body": "bar",
                             "userId": "1", "id": "1"}

@when(u'Send PUT HTTP request')
def step_impl(context):
    response = requests.put(url=api_endpoints['PUT_URL'],
                            json=request_bodies['PUT'],
                            headers=request_headers)
    response_texts['PUT'] = response.text
    print("update response :" + response.text)
    response_codes['PUT'] = response.status_code
# END PUT/UPDATE

# START DELETE
@given(u'I Set DELETE posts api endpoint for "{id}"')
def step_impl(context, id):
    api_endpoints['DELETE_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['DELETE_URL'])

@when(u'I Send DELETE HTTP request')
def step_impl(context):
    response = requests.delete(url=api_endpoints['DELETE_URL'])
    response_texts['DELETE'] = response.text
    print("DELETE response :" + response.text)
    response_codes['DELETE'] = response.status_code
# END DELETE

⚠️ Hinweis: Vergleichen von Statuscodes mit == statt is Angelegenheiten. Die is Der Operator prüft die Identität eines Objekts, nicht dessen Gleichheit. Python 3.8 und später erhöhen Syntaxwarnung: „ist“ mit einem Literal für dieses Muster. Jede Aussage, die als geschrieben wird assert code is 201 sollte umgeschrieben werden als assert code == 201.

Ausführen der Tests

Die Entwicklung des Testskripts ist abgeschlossen, also führen wir die Tests aus. Geben Sie folgenden Befehl in der Kommandozeile ein, um die Feature-Datei auszuführen:

:: Run a single feature file with the pretty formatter
behave -f pretty features\feature_files_folder\Sample_REST_API_Testing.feature

:: Run every feature in the project
behave

Die Testergebnisse werden wie folgt angezeigt:

Ausführen der Tests

Berichtsanzeige auf der Konsole

Die Konsolenausgabe ist während der Entwicklung praktisch, Stakeholder bevorzugen jedoch in der Regel einen lesbaren Bericht, den Allure bietet.

Reports

Installieren Sie zunächst den Allure Behave-Formatter und das Allure-Befehlszeilentool. Der Formatter ist ein Python Paket; das Befehlszeilentool ist ein separater Download, der in der Beschreibung enthalten ist. Allure Report-Dokumentation.

:: 1. Install the formatter
pip install allure-behave

:: 2. Run the tests and write raw results to a folder
behave -f allure_behave.formatter:AllureFormatter -o reports/allure-results features/

:: 3. Render and open the HTML report
allure serve reports/allure-results

Dadurch wird der Testbericht in einem übersichtlichen und informativen Format wie diesem generiert:

Reports

Testbericht im HTML-Format

Testbericht im HTML-Format

Testbericht mit Anzeige der einzelnen Szenarioergebnisse

Eine funktionierende Suite ist nur dann nützlich, wenn sie auch bei wachsender API lesbar bleibt. Genau hier zahlt sich Disziplin bei der Feature-Dateiverwaltung aus.

Best Practices für das Schreiben von Behave-Feature-Dateien

Eine Behave-Suite verliert schnell an Leistung, wenn Szenarien Klicks und Nutzdaten anstatt Verhalten beschreiben. Die folgenden Vorgehensweisen sorgen dafür, dass Feature-Dateien für die relevanten Stakeholder lesbar bleiben und gleichzeitig die Lesbarkeit gewährleisten.ping die Python Für Ingenieure wartungsfreundliche Ebene.

  1. Beschreiben Sie das Verhalten, nicht die Implementierung. Schreiben Sie „Vorausgesetzt, der Kunde verfügt über ausreichend Guthaben“, nicht „Vorausgesetzt, die Spalte ‚Guthaben‘ entspricht 500“. Die Feature-Datei beschreibt die Systemfunktion; die Step-Datei beschreibt die Überprüfung.
  2. Pro Szenario gilt ein einheitliches Verhalten. Ein Szenario, das einen Statuscode, einen Antworttext und eine Datenbankzeile ausgibt, sind eigentlich drei Szenarien. Durch die Aufteilung lassen sich Fehler direkt auf die Ursache zurückführen.
  3. Gemeinsame Einstellungen in den Hintergrund verschieben. Das obige Beispiel verwendet Background: Given I set sample REST API url so dass jedes Szenario die Basis erbt. URL ohne es zu wiederholen.
  4. Parametrisieren statt duplizieren. Geschränkte Platzhalter wie z. B. "{request_name}" Eine einzige Schrittfunktion dient der Ausführung von GET-, PUT- und DELETE-Assertions. Daher benötigt das Beispiel deutlich weniger Schrittdefinitionen als Szenarien.
  5. Verwenden Sie den Szenario-Gliederungsbogen für Datenvariationen. Wenn dasselbe Verhalten für mehrere Eingaben nachgewiesen werden muss, Examples: Die Tabelle ist übersichtlicher als kopierte Szenarien.
  6. Vermeiden Sie Abhängigkeiten zwischen Szenarien. Behave garantiert nicht, dass in jeder Konfiguration ein POST-Szenario vor einem GET-Szenario ausgeführt wird. Jedes Szenario sollte den benötigten Zustand erzeugen.
  7. Ersetze globale Variablen auf Modulebene durch Kontextvariablen. Das Beispiel speichert Endpunkte und Antworten aus Gründen der Übersichtlichkeit in Modulwörterbüchern. In Produktionsumgebungen sollten sie in Behave gespeichert werden. context Das Objekt sorgt dafür, dass der Zustand zwischen den Szenarien sauber zurückgesetzt wird.
  8. Tags für Szenarien zur selektiven Ausführung. Schlagwörter wie @smoke or @regression erlauben behave --tags=@smoke, wodurch ein schnelles Feedback in der Pipeline gewährleistet wird.

Die Anwendung dieser Vorgehensweisen ab der ersten Feature-Datei sorgt dafür, dass eine BDD-Suite weit über die anfänglichen CRUD-Beispiele hinaus wertvoll bleibt. Teams, die diesen Ansatz weiterentwickeln, kombinieren ihn oft mit Cucumber für JVM-Projekte oder Postman für explorative API-Prüfungen.

Häufig gestellte Fragen

Beide lesen Gherkin-Feature-Dateien. Cucumber bindet Schritte an Java, Ruby oder JavaScript, while Behave bindet sie an PythonWählen Sie diejenige aus, die zu Ihrem Anwendungs-Stack passt, damit die Schrittdefinitionen vorhandene Test-Utilities wiederverwenden können.

Ja. Behave gibt einen Exit-Code ungleich Null zurück, wenn ein Szenario fehlschlägt. JenkinsGitLab CI oder GitHub Actions können den Build automatisch abbrechen. Veröffentlichen Sie den Allure-Ergebnisordner als Build-Artefakt für die Berichterstellung.

Ja. AI Assistenten entwerfen Given-When-Then-Szenarien anhand von User Stories oder einer OpenAPI-Spezifikation. RevÜberprüfen Sie die Ausgabe auf fehlende negative Fälle und Geschäftsregeln, da generierte Szenarien oft nur den Normalfall abdecken.

KI-Tools erkennen doppelte oder nahezu identische Szenarien, schlagen wiederverwendbare parametrisierte Schritte vor und gruppieren unzuverlässige Fehler nach wahrscheinlicher Ursache. Dies reduziert den manuellen Prüfaufwand, insbesondere bei der zunehmenden Anzahl von Feature-Dateien in verschiedenen Teams.

Das Token wird einmalig im `before_all`-Hook von `environment.py` abgerufen, im Kontextobjekt gespeichert und in jedem Anfrageschritt als Authorization-Header angehängt. Anmeldeinformationen dürfen nur in Umgebungsvariablen und niemals in Feature-Dateien gespeichert werden.

Fassen Sie diesen Beitrag mit folgenden Worten zusammen: