Hvad er BDD-testning? Adfærdsdrevet udviklingsramme
⚡ Smart opsummering
BDD-testning beskriver applikationsadfærd i et almindeligt "Givet-When-Then"-sprog, så analytikere, udviklere og testere deler én specifikation. Denne gennemgang anvender denne tilgang til REST API-testning med Behave, Python framework, der dækker opsætning, funktionsfiler, trinvise implementeringer, udførelse og rapportering.
Hvad er BDD-test (Behavior Driven Development)?
BDD (Adfærdsdrevet udvikling) Test er en teknik til agil softwareudvikling og er en udvidelse af TDD, dvs. testdrevet udvikling. I BDD skrives testcases i et naturligt sprog, som selv ikke-programmører kan læse.
Hvordan fungerer BDD-test?
Forestil dig, at du har fået til opgave at oprette et pengeoverførselsmodul i en netbankapplikation.
Der er flere måder at teste det på:
- Pengeoverførsel skal finde sted, hvis der er tilstrækkelig saldo på kildekontoen
- Pengeoverførsel bør finde sted, hvis destinations-a/c-oplysningerne er korrekte
- Pengeoverførsel skal finde sted, hvis den indtastede transaktionsadgangskode / RSA-kode / sikkerhedsgodkendelse er korrekt.
- Pengeoverførsel skal finde sted, selvom det er en helligdag
- Pengeoverførsel bør finde sted på en fremtidig dato som fastsat af kontohaveren
Testscenarie bliver mere detaljeret og kompleks, efterhånden som vi overvejer yderligere funktioner såsom at overføre beløb X i et interval på Y dage eller måneder, stoppeping en planlagt overførsel, når det samlede beløb når Z, og så videre.
Udvikleres generelle tendens er at udvikle funktioner og skrive testkode senere. Som det fremgår af ovenstående tilfælde, Test sag Udviklingen her er kompleks, så udvikleren vil udskyde Test indtil udgivelsen, hvorefter der udføres hurtig, men ineffektiv testning.
For at overkomme dette problem blev adfærdsdrevet udvikling (BDD) udtænkt. Det gør hele testprocessen nemmere for en udvikler.
I BDD skal det, du skriver, gå ind i Givet-Hvornår-Så trin. Lad os betragte det samme eksempel ovenfor 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
Denne formular er nem at skrive, læse og forstå. Den dækker alle mulige testcases for pengeoverførselsmodulet og kan hurtigt ændres for at imødekomme flere. Den kan også læses som levende dokumentation for modulet. Fordi BDD voksede ud af TDD, er forskellen mellem de to værd at afklare, før man går videre til værktøjsdesign.
BDD vs TDD: Vigtigste forskelle
Begge praksisser er testorienterede, og begge forkorter feedback-loops. De adskiller sig i, hvem der skriver testene, hvilket sprog disse tests bruger, og hvilket lag af applikationen de beskriver.
| Aspect | TDD (Testdrevet Udvikling) | BDD (Behavior Driven Development) |
|---|---|---|
| Fokus | Hvordan en kodeenhed fungerer internt | Hvordan systemet opfører sig fra brugerens perspektiv |
| Sprog | Programmeringssprogspåstande | Natursprogede givne-hvornår-så-scenarier |
| Primær forfatter | Udvikler | Forretningsanalytiker, produktejer, tester og udvikler sammen |
| Readable af ikke-programmører | Ingen | Ja |
| Typisk omfang | Enhedsniveau | Funktion, API og acceptniveau |
| Almindelige værktøjer | JUnit, pytest, NUnit | Opføre sig, Cucumber, SpecFlow, JBehave |
De to komplementerer snarere end konkurrerer. Hold holder ofte TDD til design på enhedsniveau og tilføj BDD-scenarier ovenpå for at beskrive den adfærd, som interessenter rent faktisk godkender. Da REST-slutpunkter sidder præcis på det acceptlag, er de et naturligt match til BDD.
Hvad er REST API-testning?
Efterhånden som REST er blevet en populær stil til at bygge API'er, er det blevet lige så vigtigt at automatisere REST API-testcases sammen med UI-testcases. API-test involverer testning af CRUD (Create-Read-Update-Delete) handlinger med metoderne henholdsvis POST, GET, PUT og DELETE.
Hvad er Behave?
Behave er en af de populære Python BDD-testrammer. Sådan fungerer Behave:
- Funktionsfiler skrives af din forretningsanalytiker, sponsor eller den, der ejer adfærdsscenarierne. En funktionsfil bruger et naturligt sprogformat, der beskriver en funktion eller en del af en funktion med repræsentative eksempler på forventede resultater.
- Disse scenarietrin er knyttet til trinimplementeringer skrevet i Python.
- Valgfrit kan miljøkontroller køre kode før og efter trin, scenarier, funktioner eller hele kørslen.
Når rollerne for hver fil er klare, kan frameworket nu installeres.
Opsætning af BDD Testing Framework Behave on Windows
Installation:
- Download og installer Python 3 fra https://www.python.org/
- Udfør følgende kommando ved kommandoprompten for at installere Behave
pip install behave- SDI: PyCharm Community Edition bruges her — https://www.jetbrains.com/pycharm/download/
Projektopsætning:
- Opret et nyt projekt
- Opret følgende mappestruktur
Skærmbilledet ovenfor viser det layout, Behave forventer: a funktioner mappe, der indeholder funktionsfilerne, og en indlejret trin kataloget, der indeholder Python implementeringer. Behave opdager begge ved navn, så mappenavnene skal stemme nøjagtigt overens.
Funktionsfiler:
Lad os nu bygge funktionsfilen Sample_REST_API_Testing.feature, hvor funktionen er CRUD-operationer på 'indlæg'-tjenesten.
Dette eksempel bruger https://jsonplaceholder.typicode.com/ indlæg eksempel REST-tjeneste, en gratis falsk API, der accepterer skriveanmodninger og returnerer realistiske svar uden at bevare ændringerne.
Eksempel POST-scenarie
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å samme måde kan du skrive de resterende scenarier som følger:
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"
Trin implementering
Nu kan du skrive implementeringer i de funktionstrin, der bruges i scenarierne ovenfor. Python filer i mappen "steps".
Behave-frameworket identificerer step-funktionen ved at matche dekoratører mod feature-filprædikatet. For eksempel en I betragtning af prædikat i et featurefilscenarie søger efter en trinfunktion, der bærer @given dekoratør. Den samme matchning sker for Når og DerefterI tilfælde af 'Men' og 'Og' tager trinfunktionen den samme dekorator som dens foregående trin. Hvis 'Og' f.eks. følger efter en given, er den matchende trinfunktionsdekorator @given.
For eksempel kan When-trinnet for POST implementeres som følger. Bemærk hvordan "application/json" sendes fra feature-filen til den afstivede pladsholder — dette kaldes 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å samme måde gælder implementeringen af de andre trin i trinnet Python filen ser sådan ud:
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
⚠️ Bemærk: Sammenligning af statuskoder med == snarere end is sager. Det is operatoren tester objektidentitet, ikke lighed, og Python 3.8 og senere hæve en SyntaksAdvarsel: "er" med en bogstavelig for det mønster. Enhver påstand skrevet som assert code is 201 bør omskrives som assert code == 201.
Afvikling af testene
Udviklingen af testscriptet er færdig, så lad os køre testene. Udfør følgende kommando ved kommandoprompten for at køre 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
Dette viser resultaterne af testens udførelse som følger:
Rapportvisning på konsollen
Konsoloutput er praktisk under udvikling, men interessenter foretrækker normalt en læsbar rapport, hvilket Allure leverer.
Rapporter
Installer først Allure Behave-formateringsværktøjet og Allure-kommandolinjeværktøjet. Formateringsværktøjet er en Python pakke; kommandolinjeværktøjet er en separat download beskrevet i Dokumentation af 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
Dette genererer testresultatrapporten i et præsentabelt og informativt format som dette:
Testrapport i HTML-format
Testrapport, der viser individuelle scenarieresultater
En fungerende suite er kun nyttig, hvis den forbliver læsbar, efterhånden som API'en vokser, hvilket er hvor disciplin inden for funktionsfiler betaler sig.
Bedste praksis for at skrive Behave Feature Files
En Behave-suite forringes hurtigt, når scenarier beskriver klik og data i stedet for adfærd. Fremgangsmåderne nedenfor holder funktionsfiler læsbare for forretningsinteressenter, samtidig med at de holdesping og Python lag, der kan vedligeholdes af ingeniører.
- Beskriv adfærd, ikke implementering. Skriv "Forudsat at en kunde har tilstrækkelig saldo", ikke "Forudsat at saldokolonnen er lig med 500". Funktionsfilen angiver, hvad systemet gør; trinfilen angiver, hvordan det kontrolleres.
- Behold én adfærd pr. scenarie. Et scenarie, der hævder en statuskode, en svartekst og en databaserække, er i virkeligheden tre scenarier. Opdeling af dem får fejl til at pege direkte på årsagen.
- Flyt delt opsætning til baggrunden. Eksemplet ovenfor bruger
Background: Given I set sample REST API urlså hvert scenarie arver grundlaget URL uden at gentage det. - Parameteriser i stedet for at duplikere. Afstivede pladsholdere såsom
"{request_name}"Lad én trinfunktion betjene GET-, PUT- og DELETE-påstande, hvilket er grunden til, at eksemplet kræver langt færre trindefinitioner end scenarier. - Brug scenarieoversigt til datavariationer. Når den samme adfærd skal bevises for flere input,
Examples:Tabellen er tydeligere end kopierede scenarier. - Undgå afhængigheder mellem scenarier. Behave garanterer ikke, at et POST-scenarie kører før et GET-scenarie i alle konfigurationer. Hvert scenarie bør oprette den tilstand, det har brug for.
- Erstat globaler på modulniveau med kontekst. Eksemplet gemmer slutpunkter og svar i modulordbøger for at overskue det. I produktionspakker skal de gemmes på Behaves
contextobjekt, så tilstanden nulstilles præcist mellem scenarier. - Tag scenarier for selektive kørsler. Tags såsom
@smokeor@regressiontilladebehave --tags=@smoke, hvilket holder pipeline-feedbacken hurtig.
Ved at anvende disse vaner fra den første funktionsfil bevares en BDD-suites værdifulde værdi langt ud over de indledende CRUD-eksempler. Teams, der udvider denne tilgang yderligere, kombinerer den ofte med Cucumber til JVM-projekter eller Postman til udforskende API-tjek.







