Che cos'è il BDD Testing? Framework di sviluppo guidato dal comportamento
⚡ Riepilogo intelligente
Il test BDD descrive il comportamento dell'applicazione in un linguaggio semplice Given-When-Then in modo che analisti, sviluppatori e tester condividano una specifica. Questa guida applica tale approccio al test delle API REST con Behave, il Python Framework che comprende configurazione, file di funzionalità, implementazione dei passaggi, esecuzione e reporting.
Cos'è il test BDD (sviluppo comportamentale)?
Test BDD (sviluppo comportamentale). BDD è una tecnica di sviluppo software agile ed è un'estensione del TDD, ovvero dello sviluppo guidato dai test. Nel BDD, i casi di test sono scritti in un linguaggio naturale che può essere compreso anche da chi non è programmatore.
Come funziona il test BDD?
Immagina di dover creare un modulo per i trasferimenti di fondi in un'applicazione di internet banking.
Esistono diversi modi per testarlo:
- Il trasferimento dei fondi dovrebbe avvenire solo se il conto di origine dispone di un saldo sufficiente.
- Il trasferimento dei fondi dovrebbe avvenire se i dettagli del conto di destinazione sono corretti
- Il trasferimento dei fondi dovrebbe avvenire solo se la password di transazione, il codice RSA e le credenziali di sicurezza inserite dall'utente sono corrette.
- Il trasferimento dei fondi deve essere effettuato anche nei giorni festivi.
- Il trasferimento dei fondi dovrebbe avvenire in una data futura stabilita dal titolare del conto
Migliori Scenario di prova diventa più elaborato e complesso man mano che consideriamo caratteristiche aggiuntive come il trasferimento dell'importo X per un intervallo di Y giorni o mesi, l'arrestoping un trasferimento programmato al raggiungimento del totale Z, e così via.
La tendenza generale degli sviluppatori è quella di sviluppare le funzionalità e scrivere il codice di test in un secondo momento. Come è evidente nel caso sopra riportato, Test Case lo sviluppo qui è complesso, quindi lo sviluppatore lo rimanderà Collaudo fino al rilascio, momento in cui vengono effettuati test rapidi ma inefficaci.
Per superare questo problema, è stato ideato il Behavior Driven Development (BDD), che semplifica l'intero processo di test per lo sviluppatore.
In BDD, qualunque cosa scrivi deve entrare Dato-Quando-Allora passaggi. Consideriamo lo stesso esempio precedente in 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
Questo modulo è facile da scrivere, leggere e comprendere. Copre ogni possibile caso di test per il modulo di trasferimento fondi e può essere modificato rapidamente per includerne altri. Si presenta inoltre come una documentazione dinamica del modulo. Poiché il BDD deriva dal TDD, vale la pena chiarire la differenza tra i due prima di passare agli strumenti.
BDD vs TDD: differenze chiave
Entrambe le pratiche sono basate sui test e accorciano i cicli di feedback. Differiscono per chi scrive i test, per il linguaggio di programmazione utilizzato e per il livello dell'applicazione che descrivono.
| Aspetto | TDD (Sviluppo guidato dai test) | BDD (sviluppo guidato dal comportamento) |
|---|---|---|
| Focus | Come funziona internamente un'unità di codice | Come si comporta il sistema dal punto di vista dell'utente |
| Lingue disponibili | Asserzioni del linguaggio di programmazione | Scenari in linguaggio naturale Dato-Quando-Allora |
| Autore principale | Sviluppatore | Analista aziendale, responsabile prodotto, tester e sviluppatore, tutto insieme. |
| Readable da non programmatori | Non | Si |
| Ambito tipico | Livello dell'unità | Funzionalità, API e livello di accettazione |
| Strumenti comuni | JUnit, pytest, NUnit | Comportarsi, Cucumber, SpecFlow, JBehave |
I due sono complementari piuttosto che in competizione. Le squadre comunemente mantengono TDD per la progettazione a livello di unità e aggiungere scenari BDD per descrivere il comportamento che le parti interessate effettivamente approvano. Poiché gli endpoint REST si trovano esattamente a quel livello di accettazione, si adattano naturalmente al BDD.
Cos'è il test dell'API REST?
Poiché REST è diventato uno stile popolare per la creazione di API, automatizzare i casi di test delle API REST insieme ai casi di test dell'interfaccia utente è diventato altrettanto importante. REST Test dell'API implica il test delle azioni CRUD (Create-Read-Update-Delete) con i metodi POST, GET, PUT e DELETE rispettivamente.
Cos'è Comportarsi?
Comportarsi è uno dei popolari Python Framework di test BDD. Ecco come funziona Behave:
- I file delle funzionalità vengono redatti dal Business Analyst, dallo Sponsor o da chiunque sia responsabile degli scenari di comportamento. Un file delle funzionalità utilizza un formato in linguaggio naturale per descrivere una funzionalità, o parte di essa, con esempi rappresentativi dei risultati attesi.
- Questi passaggi dello scenario sono mappati alle implementazioni dei passaggi scritte in Python.
- Facoltativamente, i controlli ambientali eseguono il codice prima e dopo passaggi, scenari, funzionalità o l'intera esecuzione.
Ora che i ruoli di ciascun file sono chiari, è possibile installare il framework.
Configurazione del framework di test BDD Comportarsi bene Windows
Installazione:
- Scarica e installa Python 3 da https://www.python.org/
- Eseguire il seguente comando al prompt dei comandi per installare Behave
pip install behave- QUI: PyCharm Qui viene utilizzata la Community Edition — https://www.jetbrains.com/pycharm/download/
Configurazione del progetto:
- Creare un nuovo progetto
- Creare la seguente struttura di directory
Lo screenshot qui sopra mostra il layout previsto da Behave: un Caratteristiche directory contenente i file delle funzionalità e una directory nidificata passi directory contenente il Python implementazioni. Behave le rileva entrambe tramite il nome, quindi i nomi delle cartelle devono corrispondere esattamente.
File di funzionalità:
Ora procediamo alla creazione del file delle funzionalità. Esempio_REST_API_Testing.feature, con la funzionalità che consiste in operazioni CRUD sul servizio 'posts'.
Questo esempio utilizza il https://jsonplaceholder.typicode.com/ Questo post presenta un servizio REST di esempio, una finta API gratuita che accetta richieste di scrittura e restituisce risposte realistiche senza salvare le modifiche.
Scenario POST di esempio
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
Analogamente, è possibile scrivere gli scenari rimanenti come segue:
Esempio_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"
Attuazione delle fasi
Ora, per le fasi delle funzionalità utilizzate negli scenari precedenti, è possibile scrivere implementazioni in Python file all'interno della directory “steps”.
Il framework Behave identifica la funzione step confrontando i decoratori con il predicato del file feature. Ad esempio, un Dato predicato in uno scenario di file di funzionalità cerca una funzione di passaggio che trasporta il @given decoratore. Lo stesso abbinamento avviene per Quando and Poi. Nel caso di 'But' e 'And', la funzione step prende lo stesso decoratore del passo precedente. Ad esempio, se 'And' segue un Given, il decoratore della funzione step corrispondente è @given.
Ad esempio, il passaggio When per POST può essere implementato come segue. Nota come "application/json" viene passato dal file delle caratteristiche al segnaposto tra parentesi graffe: questo processo è chiamato parametrizzazione.
# 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
Allo stesso modo, l'implementazione degli altri passaggi nella fase Python il file apparirà così:
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
⚠️ Nota: Confrontando i codici di stato con == anziché is questioni. Il is L'operatore verifica l'identità dell'oggetto, non l'uguaglianza, e Python 3.8 e successivi sollevano un SyntaxWarning: “is” con un letterale per quel modello. Qualsiasi affermazione scritta come assert code is 201 dovrebbe essere riscritto come assert code == 201.
Esecuzione dei test
Lo sviluppo dello script di test è completo, quindi eseguiamo i test. Esegui il seguente comando nel prompt dei comandi per avviare il file feature:
:: 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
Di seguito vengono visualizzati i risultati dell'esecuzione del test:
Visualizzazione del report sulla console
L'output della console è comodo durante lo sviluppo, ma le parti interessate di solito preferiscono un report leggibile, che Allure è in grado di fornire.
Report
Innanzitutto, installa il formattatore Allure Behave e lo strumento da riga di comando Allure. Il formattatore è un Python pacchetto; lo strumento da riga di comando è un download separato descritto nel Documentazione del rapporto 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
Questo genera il report dei risultati del test in un formato presentabile e informativo come questo:
Rapporto di prova in formato HTML
Rapporto di prova che mostra il risultato del singolo scenario
Una suite di funzionalità funzionante è utile solo se rimane leggibile man mano che l'API si evolve, ed è qui che la disciplina nella gestione dei file di funzionalità ripaga.
Migliori pratiche per la scrittura di file di funzionalità di comportamento
Una suite Behave si degrada rapidamente quando gli scenari descrivono clic e payload invece del comportamento. Le pratiche seguenti mantengono i file delle funzionalità leggibili per gli stakeholder aziendali mentre mantengonoping , il Python livello gestibile dagli ingegneri.
- Descrivi il comportamento, non l'implementazione. Scrivi "Dato che un cliente ha un saldo sufficiente", non "Dato che la colonna del saldo è uguale a 500". Il file delle funzionalità descrive cosa fa il sistema; il file dei passaggi descrive come viene verificato.
- Mantenere un solo comportamento per scenario. Uno scenario che prevede un codice di stato, un corpo di risposta e una riga nel database è in realtà composto da tre scenari distinti. Suddividendoli, gli errori vengono indirizzati direttamente alla causa.
- Sposta la configurazione condivisa in background. L'esempio sopra utilizza
Background: Given I set sample REST API urlin modo che ogni scenario erediti la base URL senza ripeterlo. - Parametrizza invece di duplicare. Segnaposto rinforzati come
"{request_name}"Lasciamo che una singola funzione gestisca le asserzioni GET, PUT e DELETE, motivo per cui l'esempio richiede molte meno definizioni di passaggi rispetto agli scenari. - Utilizzare la funzione "Scenario Outline" per le variazioni dei dati. Quando lo stesso comportamento deve essere dimostrato per diversi input, un
Examples:La tabella è più chiara degli scenari copiati. - Evitare dipendenze tra scenari diversi. Behave non garantisce che uno scenario POST venga eseguito prima di uno scenario GET in ogni configurazione. Ogni scenario dovrebbe creare lo stato di cui ha bisogno.
- Sostituire le variabili globali a livello di modulo con il contesto. L'esempio memorizza endpoint e risposte nei dizionari dei moduli per brevità. Nelle suite di produzione, memorizzali su Behave
contextoggetto in modo che lo stato si ripristini correttamente tra gli scenari. - Etichettare gli scenari per le esecuzioni selettive. Etichette come
@smokeor@regressionconsentirebehave --tags=@smoke, il che mantiene rapido il feedback della pipeline.
Applicare queste abitudini fin dal primo file di funzionalità mantiene una suite BDD utile ben oltre gli esempi CRUD iniziali. I team che estendono ulteriormente questo approccio spesso lo abbinano a Cucumber per progetti JVM o Postman per verifiche esplorative delle API.







