Co je BDD testování? Rámec pro behaviorálně řízený vývoj

⚡ Chytré shrnutí

Testování BDD popisuje chování aplikace v jednoduchém jazyce typu „daný-když-pak“, takže analytici, vývojáři a testeři sdílejí jednu specifikaci. Tento návod aplikuje tento přístup na testování REST API pomocí funkce Behave, tedy funkce Behave. Python framework, zahrnující nastavení, soubory funkcí, implementace kroků, provádění a reporting.

  • 🗣️ Základní princip: Behaviorálně řízený vývoj rozšiřuje Test-Driven Development (vývoj řízený testováním) tím, že každý scénář vyjadřuje v přirozeném jazyce, který si mohou přečíst a schválit i neprogramátoři.
  • 🧱 Struktura scénáře: Metoda Given (Dáno) stanoví předběžné podmínky, metoda When (Když) provede akci a metoda Then (Pak) potvrdí výsledek, zatímco metoda And nebo But dědí dekorátor předchozího kroku.
  • 🌐 Pokrytí REST: Příklad demonstruje plné chování CRUD prostřednictvím operací POST, GET, PUT a DELETE pro veřejnou službu příspěvků jsonplaceholder.
  • 🐍 Nastavení rámce: Instalace Behave přes pip Python 3 a očekává adresář features obsahující soubory feature a balíček steps s implementacemi.
  • 🔗 Kroková vazba: Dekorátory jako @given, @when a @then odpovídají textu funkce a zástupné symboly v závorkách předávají hodnoty ze scénáře do Python funkce.
  • ▶ </ s> Provedení: Hezký formátovač vypíše pro každý krok v konzoli barevně odlišený výsledek úspěšného nebo neúspěšného provedení.
  • 📊 Hlášení: Formátovací program Allure-behave zapisuje strojově čitelné výsledky, které příkazový řádek Allure vykreslí jako prohlížetelný HTML report.

Co je BDD testování

Co je testování BDD (Behavior Driven Development)?

BDD (Behavior-driven development) Testování je technika agilního vývoje softwaru a rozšíření TDD, tj. Test Driven Development (vývoj řízený testy). V BDD jsou testovací případy psány v přirozeném jazyce, který dokážou číst i neprogramátoři.

Jak funguje testování BDD?

Představte si, že máte za úkol vytvořit modul pro převod finančních prostředků v aplikaci Net Banking.

Existuje několik způsobů, jak to otestovat:

  1. Převod finančních prostředků by měl proběhnout, pokud je na zdrojovém účtu dostatečný zůstatek.
  2. Převod prostředků by měl proběhnout, pokud jsou podrobnosti o klimatizaci správné
  3. Převod finančních prostředků by měl proběhnout, pokud je heslo transakce / kód RSA / bezpečnostní ověření zadané uživatelem správné.
  4. Převod finančních prostředků by měl proběhnout i v případě státního svátku.
  5. Převod prostředků by měl proběhnout k budoucímu datu stanovenému majitelem účtu

Jedno Scénář testu stává se propracovanějším a komplexnějším, když vezmeme v úvahu další prvky, jako je převod částky X po dobu Y dnů nebo měsíců, zastaveníping plánovaný převod, když celková částka dosáhne Z, a tak dále.

Obecnou tendencí vývojářů je vyvíjet funkce a testovací kód psát až později. Jak je patrné z výše uvedeného případu, Testovací případ vývoj je zde složitý, takže developer odloží Testování až do vydání, kdy se provádí rychlé, ale neúčinné testování.

Aby se tento problém překonal, byl vytvořen Behaviorálně řízený vývoj (BDD). Ten vývojářům usnadňuje celý proces testování.

V BDD, cokoli napíšete, musí jít do Daný-když-pak kroky. Uvažujme stejný příklad výše v 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

Tento formulář se snadno píše, čte a rozumí mu. Zahrnuje všechny možné testovací případy pro modul převodu finančních prostředků a lze jej rychle upravit, aby vyhovoval většímu počtu požadavků. Zároveň se čte jako živá dokumentace k modulu. Protože BDD vzešel z TDD, je vhodné si rozdíl mezi nimi vyjasnit, než se přesuneme k nástrojům.

BDD vs. TDD: Klíčové rozdíly

Oba postupy jsou zaměřeny na testování jako první a oba zkracují zpětnovazební smyčky. Liší se v tom, kdo testy píše, jaký jazyk tyto testy používají a kterou vrstvu aplikace popisují.

Vzhled TDD (vývoj řízený testy) BDD (vývoj řízený chováním)
Soustředit Jak interně funguje jednotka kódu Jak se systém chová z pohledu uživatele
Jazyk Tvrzení programovacího jazyka Scénáře „Dané-Když-Pak“ v přirozeném jazyce
Hlavní autor Vývojář Obchodní analytik, produktový vlastník, tester a vývojář společně
Readable od neprogramátorů Ne Ano
Typický rozsah Úroveň jednotky Funkce, API a úroveň akceptace
Běžné nástroje JUnit, pytest, NUnit Chovej se, Cucumber, SpecFlow, JBehave

Tyto dvě věci se spíše doplňují než soupeří. Týmy si obvykle udržují TDD pro návrh na úrovni jednotek a přidat scénáře BDD, které popisují chování, které zúčastněné strany skutečně schvalují. Protože koncové body REST se nacházejí přesně na této akceptační vrstvě, jsou přirozeně vhodné pro BDD.

Co je testování REST API?

Vzhledem k tomu, že se REST stal populárním stylem pro tvorbu API, automatizace testovacích případů REST API spolu s testovacími případy uživatelského rozhraní se stala stejně důležitou. REST API testování zahrnuje testování akcí CRUD (Create-Read-Update-Delete) s metodami POST, GET, PUT a DELETE.

Co je Behave?

Behave je jedním z populárních Python Testovací frameworky BDD. Zde je návod, jak funguje Behave:

  • Soubory funkcí jsou napsány vaším obchodním analytikem, sponzorem nebo kýmkoli, kdo je vlastníkem scénářů chování. Soubor funkcí používá formát přirozeného jazyka popisující funkci nebo část funkce s reprezentativními příklady očekávaných výsledků.
  • Tyto kroky scénáře jsou namapovány na implementace kroků napsané v Python.
  • Volitelně ovládací prvky prostředí spouštějí kód před a po krocích, scénářích, funkcích nebo celém spuštění.

Po vyjasnění rolí jednotlivých souborů lze nyní framework nainstalovat.

Nastavení testovacího rámce BDD Chovej se na Windows

Instalace:

Nastavení projektu:

  • Vytvořit nový projekt
  • Vytvořte následující adresářovou strukturu

Nastavení projektu

Snímek obrazovky výše ukazuje rozvržení, které Behave očekává: a funkce adresář obsahující soubory funkcí a vnořený kroky adresář, který obsahuje Python implementace. Behave vyhledává obě složky podle názvu, takže názvy složek se musí přesně shodovat.

Soubory funkcí:

Nyní sestavme soubor s funkcemi Sample_REST_API_Testing.feature, přičemž touto funkcí jsou operace CRUD na službě „příspěvky“.

Tento příklad používá https://jsonplaceholder.typicode.com/ ukázka příspěvku REST služba, bezplatné falešné API, které přijímá požadavky na zápis a vrací realistické odpovědi bez ukládání změn.

Příklad scénáře POST

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

Podobně můžete zbývající scénáře zapsat takto:

Nastavení projektu

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"

Kroky implementace

Nyní můžete pro kroky funkcí použité ve výše uvedených scénářích napsat implementace v Python soubory v adresáři „steps“.

Framework Behave identifikuje funkci step porovnáváním dekorátorů s predikátem souboru funkcí. Například Vzhledem k Predikát ve scénáři souboru funkcí hledá krokovou funkci nesoucí @given dekoratér. Stejné párování se děje i pro Kdy a PakV případě 'But' a 'And' používá funkce step stejný dekorátor jako její předchozí krok. Například pokud 'And' následuje za daným, odpovídající dekorátor funkce step je @given.

Například krok When pro POST lze implementovat následovně. Všimněte si, jak "application/json" se předává ze souboru prvků do zástupného symbolu v závorkách – tomu se říká parametrizace.

# 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

Podobně implementace ostatních kroků v kroku Python soubor bude vypadat takto:

Kroky implementace

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

⚠️ Poznámka: Porovnání stavových kódů s == spíše než is záležitosti. Ten is operátor testuje identitu objektu, nikoli rovnost, a Python 3.8 a novější vyvolávají SyntaxWarning: „je“ s literálem pro daný vzor. Jakékoli tvrzení zapsané jako assert code is 201 by mělo být přepsáno jako assert code == 201.

Provádění testů

Vývoj testovacího skriptu je dokončen, takže spusťme testy. Spusťte soubor s funkcemi na příkazovém řádku spuštěním následujícího příkazu:

:: 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

Výsledky provedení testu se zobrazí takto:

Provádění testů

Zobrazení zprávy na konzole

Výstup do konzole je během vývoje pohodlný, ale zúčastněné strany obvykle preferují čitelnou zprávu, kterou poskytuje Allure.

zprávy

Nejprve nainstalujte formátovací program Allure Behave a nástroj příkazového řádku Allure. Formátovací program je Python balíček; nástroj příkazového řádku je ke stažení samostatně, popsaný v Dokumentace zprávy Allure.

:: 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

Tím se vygeneruje zpráva o výsledcích testu v prezentovatelném a informativním formátu, jako je tento:

zprávy

Testovací zpráva ve formátu HTML

Testovací zpráva ve formátu HTML

Zpráva o testu zobrazující výsledek jednotlivých scénářů

Funkční sada je užitečná pouze tehdy, pokud zůstává čitelná i s růstem API, a právě zde se vyplácí disciplína v souborech funkcí.

Nejlepší postupy pro psaní souborů funkcí Behave

Sada Behave se rychle degraduje, když scénáře popisují kliknutí a datové části místo chování. Níže uvedené postupy udržují soubory funkcí čitelné pro obchodní zainteresované strany a zároveň zachovávají...ping ο Python vrstva udržovatelná pro inženýry.

  1. Popište chování, ne implementaci. Napište „Za předpokladu, že zákazník má dostatečný zůstatek“, nikoli „Za předpokladu, že sloupec zůstatku se rovná 500“. Soubor funkcí uvádí, co systém dělá; soubor kroků uvádí, jak se to kontroluje.
  2. Zachovejte jedno chování na scénář. Scénář, který uvádí stavový kód, tělo odpovědi a řádek databáze, je ve skutečnosti tři scénáře. Jejich rozdělení umožňuje, aby selhání přímo ukazovalo na příčinu.
  3. Přesunout sdílené nastavení na pozadí. Výše uvedený příklad používá Background: Given I set sample REST API url takže každý scénář zdědí základnu URL bez opakování.
  4. Parametrizujte místo duplikace. Zástupné symboly v závorkách, jako například "{request_name}" Funkce typu „nechat jeden krok“ obsluhuje assessiony GET, PUT a DELETE, a proto tento příklad potřebuje mnohem méně definic kroků než scénáře.
  5. Pro varianty dat použijte osnovu scénáře. Pokud je nutné dokázat stejné chování pro několik vstupů, Examples: tabulka je přehlednější než kopírované scénáře.
  6. Vyhněte se závislostem mezi scénáři. Metoda Behave nezaručuje, že scénář POST se spustí před scénářem GET v každé konfiguraci. Každý scénář by měl vytvořit stav, který potřebuje.
  7. Nahraďte globální proměnné na úrovni modulů kontextem. Příklad ukládá koncové body a odpovědi do slovníků modulů pro stručnost. V produkčních sadách je ukládejte do slovníků Behave. context objekt, takže stav se mezi scénáři čistě resetuje.
  8. Označte scénáře pro selektivní spuštění. Štítky jako například @smoke or @regression povolit behave --tags=@smoke, což udržuje zpětnou vazbu v kanálu rychlou.

Aplikace těchto návyků z prvního souboru funkcí udržuje sadu BDD cennou i po uplynutí původních příkladů CRUD. Týmy, které tento přístup dále rozšiřují, jej často kombinují s... Cucumber pro JVM projekty nebo Postman pro průzkumné kontroly API.

Nejčastější dotazy

Oba čtou soubory funkcí Gherkin. Cucumber spojuje kroky s Java, Ruby nebo JavaSkript, zatímco Behave je váže k PythonVyberte ten, který odpovídá vašemu aplikačnímu stacku, aby definice kroků znovu používaly existující testovací utility.

Ano. Funkce Behave vrací nenulový ukončovací kód, když jakýkoli scénář selže, takže JenkinsAkce , GitLab CI nebo GitHub mohou sestavení automaticky selhat. Publikujte složku s výsledky Allure jako artefakt sestavení pro účely reportingu.

Ano. AI Asistenti navrhují scénáře typu „Dáno-Když-Pak“ z uživatelských příběhů nebo specifikace OpenAPI. Revzobrazit výstup pro chybějící negativní případy a obchodní pravidla, protože generované scénáře často pokrývají pouze šťastnou cestu.

Nástroje umělé inteligence detekují duplicitní nebo téměř duplicitní scénáře, navrhují opakovaně použitelné parametrizované kroky a clusterují nestabilní selhání podle pravděpodobné příčiny. To snižuje úsilí manuálního auditu, protože se soubory funkcí napříč týmy množí.

Načtěte token jednou v hooku environment.py before_all, uložte ho do objektu context a připojte jej jako hlavičku Authorization v každém kroku požadavku. Přihlašovací údaje uchovávejte v proměnných prostředí, nikdy ne v souborech funkcí.

Shrňte tento příspěvek takto: