Vad är BDD-testning? Beteendedrivet utvecklingsramverk
⚡ Smart sammanfattning
BDD-testning beskriver applikationsbeteende i ett enkelt "Givet-When-Sedan"-språk så att analytiker, utvecklare och testare delar en specifikation. Denna genomgång tillämpar den metoden på REST API-testning med Behave, Python ramverk, som täcker installation, funktionsfiler, stegimplementeringar, exekvering och rapportering.

Vad är BDD-testning (Behavior Driven Development)?
BDD (Beteendedriven utveckling) Testning är en teknik för agil mjukvaruutveckling och är en förlängning av TDD, dvs. testdriven utveckling. I BDD skrivs testfall på ett naturligt språk som även icke-programmerare kan läsa.
Hur fungerar BDD-testning?
Anta att du har fått i uppdrag att skapa en modul för överföring av medel i en nätbanksapplikation.
Det finns flera sätt att testa det:
- Fondöverföring bör ske om det finns tillräckligt saldo på källkontot
- Fondöverföring bör ske om informationen om destinationens luftkonditionering är korrekt
- Pengöverföring ska ske om transaktionslösenordet / RSA-koden / säkerhetsautentiseringen som angetts av användaren är korrekt.
- Överföring av medel ska ske även om det är en helgdag
- Fondöverföring bör ske på ett framtida datum som anges av kontoinnehavaren
Ocuco-landskapet Testscenario blir mer detaljerat och komplext när vi beaktar ytterligare funktioner som att överföra belopp X under ett intervall på Y dagar eller månader, stoppaping en schemalagd överföring när totalen når Z, och så vidare.
Utvecklare har en generell tendens att utveckla funktioner och skriva testkod senare. Som framgår av fallet ovan, Testfall utvecklingen här är komplex, så utvecklaren kommer att skjuta upp Testning tills lanseringen, varvid snabba men ineffektiva tester utförs.
För att övervinna detta problem utformades beteendedriven utveckling (BDD). Det gör hela testprocessen enklare för en utvecklare.
I BDD måste vad du än skriver gå in på Givet-När-Då steg. Låt oss betrakta samma exempel ovan i BDD:
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
Det här formuläret är lätt att skriva, läsa och förstå. Det täcker alla möjliga testfall för fondöverföringsmodulen och kan snabbt modifieras för att rymma fler. Det kan också läsas som en levande dokumentation för modulen. Eftersom BDD växte fram ur TDD är skillnaden mellan de två värd att reda ut innan man går vidare till verktygsutveckling.
BDD vs TDD: Viktiga skillnader
Båda metoderna är testorienterade och båda förkortar feedback-loopar. De skiljer sig åt i vem som skriver testerna, vilket språk de använder och vilket lager av applikationen de beskriver.
| Aspect | TDD (Testdriven utveckling) | BDD (Behavior Driven Development) |
|---|---|---|
| Fokus | Hur en kodenhet fungerar internt | Hur systemet beter sig ur användarperspektivet |
| Språk | Programmeringsspråkspåståenden | Naturligt språk Givna-När-Sedan-scenarier |
| Huvudförfattare | Developer | Affärsanalytiker, produktägare, testare och utvecklare tillsammans |
| Readable av icke-programmerare | Nej | Ja |
| Typisk omfattning | Enhetsnivå | Funktion, API och acceptansnivå |
| Vanliga verktyg | JUnit, pytest, NUnit | Bete sig, Cucumber, SpecFlow, JBehave |
De två kompletterar snarare än konkurrerar. Lagen håller vanligtvis TDD för design på enhetsnivå och lägg till BDD-scenarier ovanpå för att beskriva det beteende som intressenter faktiskt godkänner. Eftersom REST-slutpunkter sitter exakt på det acceptansskiktet passar de naturligt för BDD.
Vad är REST API-testning?
I takt med att REST har blivit en populär metod för att bygga API:er har det blivit lika viktigt att automatisera REST API-testfall tillsammans med UI-testfall. API-testning innebär att testa CRUD-åtgärder (Create-Read-Update-Delete) med metoderna POST, GET, PUT respektive DELETE.
Vad är Behave?
Behave är en av de populära Python BDD-testramverk. Så här fungerar Behave:
- Funktionsfiler skrivs av din affärsanalytiker, sponsor eller den som äger beteendescenarierna. En funktionsfil använder ett naturligt språkformat som beskriver en funktion, eller en del av en funktion, med representativa exempel på förväntade resultat.
- Dessa scenariosteg är mappade till stegimplementeringar skrivna i Python.
- Valfritt kan miljökontroller köra kod före och efter steg, scenarier, funktioner eller hela körningen.
När rollerna för varje fil är klara kan ramverket nu installeras.
Ställa in BDD Testing Framework Behave on Windows
Installation:
- Ladda ned och installera Python 3 från https://www.python.org/
- Kör följande kommando vid kommandotolken för att installera Behave
pip install behave- ID: PyCharm Community Edition används här — https://www.jetbrains.com/pycharm/download/
Projektinställning:
- Skapa ett nytt projekt
- Skapa följande katalogstruktur
Skärmdumpen ovan visar layouten som Behave förväntar sig: a pass katalog som innehåller funktionsfilerna och en kapslad steg katalogen som innehåller Python implementeringar. Behave upptäcker båda med namn, så mappnamnen måste matcha exakt.
Funktionsfiler:
Nu ska vi bygga funktionsfilen Sample_REST_API_Testing.feature, med funktionen CRUD-åtgärder på tjänsten 'inlägg'.
I detta exempel används https://jsonplaceholder.typicode.com/ inlägg exempel på REST-tjänst, ett gratis falskt API som accepterar skrivförfrågningar och returnerar realistiska svar utan att behålla ändringarna.
Exempel POST-scenario
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
På samma sätt kan du skriva de återstående scenarierna enligt följande:
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"
Steg Implementering
Nu, för funktionsstegen som används i scenarierna ovan, kan du skriva implementeringar i Python filer i katalogen "steps".
Behave-ramverket identifierar stegfunktionen genom att matcha dekoratörer mot featurefilspredikatet. Till exempel, en Med tanke på predikat i ett funktionsfilscenario söker efter en stegfunktion som bär @given dekoratör. Samma matchning sker för När och SedanI fallet med 'Men' och 'Och' tar stegfunktionen samma dekorator som dess föregående steg. Om till exempel 'Och' följer efter en given, är den matchande stegfunktionens dekorator @given.
Till exempel kan steget När för POST implementeras enligt följande. Observera hur "application/json" skickas från funktionsfilen till den spetsade platshållaren — detta kallas parametrisering.
# 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
På samma sätt gäller implementeringen av de andra stegen i steget Python filen ser ut så här:
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
⚠️ Obs: Jämföra statuskoder med == snarare än is frågor. Den is operatorn testar objektidentitet, inte likhet, och Python 3.8 och senare höja en SyntaxVarning: ”är” med en bokstav för det mönstret. Alla påståenden som skrivs som assert code is 201 bör skrivas om som assert code == 201.
Kör testerna
Utvecklingen av testskriptet är klar, så låt oss köra testerna. Kör följande kommando vid kommandotolken för att köra funktionsfilen:
:: 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
Detta visar testkörningsresultaten enligt följande:
Rapportvisning på konsolen
Konsolutdata är praktiskt under utveckling, men intressenter föredrar vanligtvis en läsbar rapport, vilket Allure tillhandahåller.
Rapport
Installera först formateringsverktyget Allure Behave och kommandoradsverktyget Allure. Formateringsverktyget är en Python paketet; kommandoradsverktyget är en separat nedladdning som beskrivs i Dokumentation av Allure-rapporten.
:: 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
Detta genererar testresultatsrapporten i ett presentabelt och informativt format som detta:
Testrapport i HTML-format
Testrapport som visar individuella scenarieresultat
En fungerande svit är bara användbar om den förblir läsbar allt eftersom API:et växer, vilket är där funktionsfilsdisciplin lönar sig.
Bästa praxis för att skriva Behave Feature-filer
En Behave-svit försämras snabbt när scenarier beskriver klick och nyttolaster istället för beteende. Metoderna nedan gör att funktionsfilerna är läsbara för affärsintressenter samtidigt som de hållsping d Python lager som är underhållbart för ingenjörer.
- Beskriv beteende, inte implementering. Skriv ”Givet att kunden har tillräckligt saldo”, inte ”Givet att saldokolumnen är lika med 500”. Funktionsfilen anger vad systemet gör; stegfilen anger hur det kontrolleras.
- Behåll ett beteende per scenario. Ett scenario som anger en statuskod, en svarstext och en databasrad är egentligen tre scenarier. Att dela upp dem gör att fel pekar direkt på orsaken.
- Flytta delade inställningar till bakgrunden. Exemplet ovan använder
Background: Given I set sample REST API urlså att varje scenario ärver basen URL utan att upprepa det. - Parametrisera istället för att duplicera. Stabila platshållare som t.ex.
"{request_name}"låt en stegfunktion hantera GET-, PUT- och DELETE-påståenden, vilket är anledningen till att exemplet behöver betydligt färre stegdefinitioner än scenarier. - Använd scenarioöversikt för datavariationer. När samma beteende måste bevisas för flera ingångar, en
Examples:Tabellen är tydligare än kopierade scenarier. - Undvik beroenden mellan scenarier. Behave garanterar inte att ett POST-scenario körs före ett GET-scenario i varje konfiguration. Varje scenario bör skapa det tillstånd det behöver.
- Ersätt globala värden på modulnivå med kontext. Exemplet lagrar slutpunkter och svar i modulordböcker för korthets skull. I produktionssviter, lagra dem på Behaves
contextobjekt så att tillståndet återställs korrekt mellan scenarier. - Taggscenarier för selektiva körningar. Taggar som
@smokeor@regressiontillåterbehave --tags=@smoke, vilket håller pipeline-feedbacken snabb.
Att tillämpa dessa vanor från den första funktionsfilen gör att en BDD-svit behåller sitt värde långt bortom de initiala CRUD-exemplen. Team som utökar denna metod ytterligare kombinerar den ofta med Cucumber för JVM-projekt eller Postman för utforskande API-kontroller.






