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.
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:
- Die Geldüberweisung sollte erfolgen, wenn auf dem Quellkonto ausreichend Guthaben vorhanden ist.
- Die Überweisung sollte erfolgen, wenn die Angaben zum Zielkonto korrekt sind.
- Die Geldüberweisung sollte erfolgen, wenn das vom Benutzer eingegebene Transaktionspasswort/RSA-Code/die Sicherheitsauthentifizierung korrekt ist.
- Geldüberweisungen sollten auch an Bankfeiertagen erfolgen.
- 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:
- Laden Sie Python 3 aus https://www.python.org/
- Führen Sie den folgenden Befehl in der Eingabeaufforderung aus, um Behave zu installieren.
pip install behave- IDEE: PyCharm Hier wird die Community Edition verwendet. https://www.jetbrains.com/pycharm/download/
Projektaufbau:
- Erstellen Sie ein neues Projekt
- Erstellen Sie die folgende Verzeichnisstruktur
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:
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:
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:
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:
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.
- 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.
- 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.
- Gemeinsame Einstellungen in den Hintergrund verschieben. Das obige Beispiel verwendet
Background: Given I set sample REST API urlso dass jedes Szenario die Basis erbt. URL ohne es zu wiederholen. - 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. - 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. - 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.
- 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.
contextDas Objekt sorgt dafür, dass der Zustand zwischen den Szenarien sauber zurückgesetzt wird. - Tags für Szenarien zur selektiven Ausführung. Schlagwörter wie
@smokeor@regressionerlaubenbehave --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.







