Mitä on BDD-testaus? Käyttäytymislähtöinen kehityskehys

⚡ Älykäs yhteenveto

BDD-testaus kuvaa sovelluksen käyttäytymistä selkeällä Given-When-Then-kielellä, jotta analyytikot, kehittäjät ja testaajat jakavat yhden määrittelyn. Tämä läpikäynti soveltaa tätä lähestymistapaa REST API -testaukseen Behave-menetelmällä. Python viitekehys, joka kattaa asennuksen, ominaisuustiedostot, vaiheiden toteutuksen, suorituksen ja raportoinnin.

  • 🗣️ Keskeinen periaate: Käyttäytymislähtöinen kehitys laajentaa testilähtöistä kehitystä ilmaisemalla jokaisen skenaarion luonnollisella kielellä, jonka myös muut kuin ohjelmoijat voivat lukea ja hyväksyä.
  • 🧱 Skenaarion rakenne: Given asettaa ennakkoehdot, When suorittaa toiminnon ja Then vahvistaa tuloksen, kun taas And tai But perivät edellisen vaiheen koristelun.
  • 🌐 REST-kattavuus: Esimerkissä käytetään POST-, GET-, PUT- ja DELETE-käskyjä julkisessa jsonplaceholder-posts-palvelussa.
  • 🐍 Kehyksen asetukset: Behave-asennukset pipin kautta Python 3 ja odottaa ominaisuushakemistoa, joka sisältää ominaisuustiedostot sekä toteutukset sisältävän steps-paketin.
  • 🔗 Vaiheen sidonta: Koristeominaisuudet, kuten @given, @when ja @then, vastaavat ominaisuustekstiä, ja hakasulkeissa olevat paikkamerkit välittävät arvot skenaariosta funktioon. Python toiminto.
  • ▶ ️ toteutus: Kaunis muotoiluohjelma tulostaa värikoodatun hyväksymis- tai hylkäystuloksen jokaisesta konsolin vaiheesta.
  • 📊 raportointi: Allure-behave-muotoilutyökalu kirjoittaa koneellisesti luettavia tuloksia, jotka Allure-komentorivi renderöi selattavana HTML-raporttina.

Mikä on BDD-testaus

Mitä on BDD (Behavior Driven Development) -testaus?

BDD (Behavior-driven Development) -testaus on ketterän ohjelmistokehityksen tekniikka ja TDD:n eli testivetoisen kehityksen laajennus. BDD:ssä testitapaukset kirjoitetaan luonnollisella kielellä, jota myös ei-ohjelmoijat osaavat lukea.

Kuinka BDD-testaus toimii?

Oletetaan, että sinulle on annettu tehtäväksi luoda varainsiirtomoduuli verkkopankkisovelluksessa.

Sen testaamiseen on useita tapoja:

  1. Rahansiirto tulisi tehdä, jos lähdetilillä on riittävästi saldoa
  2. Varainsiirto tulee suorittaa, jos kohteen ilmastointitiedot ovat oikein
  3. Rahansiirron pitäisi tapahtua, jos käyttäjän syöttämä tapahtuman salasana / RSA-koodi / turvatodennus on oikein.
  4. Rahansiirron tulisi tapahtua, vaikka olisi pankkipyhä
  5. Rahansiirron tulee tapahtua tilinhaltijan määräämänä tulevana päivänä

Testiskenaario tulee monimutkaisemmaksi ja monimutkaisemmaksi, kun otamme huomioon lisäominaisuuksia, kuten X summan siirtämisen Y päivän tai kuukauden välein, pysäytysping ajoitettu siirto, kun kokonaissumma saavuttaa Z:n, ja niin edelleen.

Kehittäjien yleinen taipumus on kehittää ominaisuuksia ja kirjoittaa testikoodi vasta myöhemmin. Kuten yllä olevasta tapauksesta käy ilmi, Testitapaus kehitys on täällä monimutkaista, joten kehittäjä lykkää Testaus julkaisuun asti, jolloin suoritetaan nopea mutta tehoton testaus.

Tämän ongelman ratkaisemiseksi kehitettiin käyttäytymislähtöinen kehitys (Behavior Driven Development, BDD). Se helpottaa koko testausprosessia kehittäjälle.

BDD:ssä kaiken, mitä kirjoitat, on mentävä sisään Annettu-Kun-Sitten vaiheet. Tarkastellaan samaa esimerkkiä yllä BDD:ssä:

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

Tätä lomaketta on helppo kirjoittaa, lukea ja ymmärtää. Se kattaa kaikki mahdolliset varainsiirtomoduulin testitapaukset ja sitä voidaan muokata nopeasti useampien tapausten mukaiseksi. Se on myös kuin moduulin elävää dokumentaatiota. Koska BDD kehittyi TDD:stä, niiden välinen ero kannattaa selvittää ennen työkaluihin siirtymistä.

BDD vs. TDD: Keskeiset erot

Molemmat käytännöt ovat testilähtöisiä ja lyhentävät palautesilmukoita. Ne eroavat toisistaan ​​siinä, kuka kirjoittaa testit, mitä kieltä testit käyttävät ja mitä sovelluksen tasoa ne kuvaavat.

Aspect TDD (testauspohjainen kehitys) BDD (Behavior Driven Development)
Focus Miten koodiyksikkö toimii sisäisesti Miten järjestelmä toimii käyttäjän näkökulmasta
Kieli Ohjelmointikielen väitteet Luonnollisen kielen annettu-kun-niin-skenaariot
Pääkirjoittaja Kehittäjä Liiketoiminta-analyytikko, tuoteomistaja, testaaja ja kehittäjä yhdessä
Readable ei-ohjelmoijien toimesta Ei Kyllä
Tyypillinen laajuus Yksikkötaso Ominaisuus, API ja hyväksymistaso
Yleiset työkalut JUnit, pytesti, NUnit Käyttäytyä, Cucumber, SpecFlow, JBehave

Nämä kaksi täydentävät toisiaan eivätkä kilpaile keskenään. Joukkueet yleensä pitävät TDD yksikkötason suunnittelua varten ja lisätä päälle BDD-skenaarioita kuvaamaan toimintaa, jonka sidosryhmät tosiasiallisesti hyväksyvät. Koska REST-päätepisteet sijaitsevat täsmälleen kyseisellä hyväksyntätasolla, ne sopivat luonnollisesti BDD:hen.

Mitä REST API -testaus on?

Koska RESTistä on tullut suosittu tyyli APIen rakentamiseen, REST API -testitapausten automatisoinnista käyttöliittymätestitapausten ohella on tullut yhtä tärkeää. API-testaus sisältää CRUD (Create-Read-Update-Delete) -toimintojen testaamisen POST-, GET-, PUT- ja DELETE-metodeilla.

Mikä on Behave?

Behave on yksi suosituimmista Python BDD-testauskehykset. Näin Behave-funktiot toimivat:

  • Ominaisuustiedostot kirjoittaa liiketoiminta-analyytikkosi, sponsorisi tai kuka tahansa, jolle käyttäytymisskenaariot kuuluvat. Ominaisuustiedosto käyttää luonnollisen kielen muotoa, joka kuvaa ominaisuutta tai ominaisuuden osaa ja esittää edustavia esimerkkejä odotetuista tuloksista.
  • Nämä skenaarion vaiheet on yhdistetty vaiheiden toteutuksiin, jotka on kirjoitettu kielellä Python.
  • Valinnaisesti ympäristökontrollit suorittavat koodia ennen vaiheita, skenaarioita, ominaisuuksia tai koko suoritusta ja niiden jälkeen.

Kun kunkin tiedoston roolit on selvitetty, kehys voidaan nyt asentaa.

BDD Testing Frameworkin määrittäminen Käyttäydy Windows

Asennus:

Projektin määritys:

  • Luo uusi projekti
  • Luo seuraava hakemistorakenne

Projektin asennus

Yllä olevassa kuvakaappauksessa näkyy asettelu, jonka Behave odottaa: a piirteet hakemisto, joka sisältää ominaisuustiedostot, ja sisäkkäinen vaiheet hakemisto, joka sisältää Python toteutukset. Behave löytää molemmat nimen perusteella, joten kansioiden nimien on oltava täsmälleen samat.

Ominaisuustiedostot:

Nyt rakennetaan ominaisuustiedosto Sample_REST_API_Testing.feature, ja ominaisuus on CRUD-toiminnot 'posts'-palvelussa.

Tämä esimerkki käyttää https://jsonplaceholder.typicode.com/ julkaisee esimerkki REST-palvelusta, ilmaisesta väärennetystä API:sta, joka hyväksyy kirjoituspyynnöt ja palauttaa realistisia vastauksia ilman muutosten tallentamista.

Esimerkki POST-skenaariosta

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

Vastaavasti voit kirjoittaa loput skenaariot seuraavasti:

Projektin asennus

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"

Toteutusvaiheet

Nyt voit kirjoittaa toteutuksia yllä olevissa skenaarioissa käytettyjen ominaisuusvaiheiden osalta Python tiedostot "steps"-hakemistossa.

Behave-kehys tunnistaa askelfunktion vertaamalla koristereita ominaisuustiedoston predikaattiin. Esimerkiksi Tietty predikaatti ominaisuustiedostoskenaariossa etsii askelfunktiota, joka sisältää @given sisustaja. Sama yhdistäminen tapahtuu myös Kun ja Sitten'Mutta'- ja 'Ja'-operaattorien tapauksessa askelfunktio käyttää samaa koristelijaa kuin edellinen askel. Esimerkiksi, jos 'Ja' seuraa annettua lauseketta, vastaava askelfunktion koristelija on @given.

Esimerkiksi POST-testin When-vaihe voidaan toteuttaa seuraavasti. Huomaa, miten "application/json" välitetään ominaisuustiedostosta hakasulkeissa olevaan paikkamerkkiin – tätä kutsutaan parametrisoinniksi.

# 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

Samoin vaiheen muiden vaiheiden toteutus Python tiedosto tulee näyttämään tältä:

Toteutusvaiheet

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

⚠️ Huomaa: Tilakoodien vertailu == sen sijaan, is asiat. is operaattori testaa objektin identiteettiä, ei yhtäsuuruutta, ja Python 3.8 ja myöhemmin nostaa Syntaksivaroitus: ”on” literaalilla kyseiselle mallille. Mikä tahansa väite, joka on kirjoitettu muodossa assert code is 201 pitäisi kirjoittaa uudelleen muotoon assert code == 201.

Testien suorittaminen

Testiskriptin kehitys on valmis, joten suoritetaan testit. Suorita seuraava komento komentokehotteessa ominaisuustiedoston suorittamiseksi:

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

Tämä näyttää testin suorituksen tulokset seuraavasti:

Testien suorittaminen

Raportin näyttö konsolissa

Konsolin tulostus on kätevää kehitysvaiheessa, mutta sidosryhmät yleensä suosivat luettavaa raporttia, kuten Allure tarjoaa.

Raportit

Asenna ensin Allure Behave -muotoiluohjelma ja Allure-komentorivityökalu. Muotoiluohjelma on Python paketti; komentorivityökalu on erillinen ladattava tiedosto, joka on kuvattu kohdassa Allure-raportin dokumentaatio.

:: 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ä luo testitulosraportin edustuskelpoisessa ja informatiivisessa muodossa, kuten tässä:

Raportit

Testiraportti HTML-muodossa

Testiraportti HTML-muodossa

Testiraportti, joka näyttää yksittäisen skenaarion tuloksen

Toimivasta tiedostosarjasta on hyötyä vain, jos se pysyy luettavana API:n kasvaessa, ja juuri siinä ominaisuustiedostojen kurinalaisuus kannattaa.

Parhaat käytännöt Behave-ominaisuustiedostojen kirjoittamiseen

Behave-sviitti heikkenee nopeasti, kun skenaariot kuvaavat klikkauksia ja hyötykuormia käyttäytymisen sijaan. Alla olevat käytännöt pitävät ominaisuustiedostot luettavina liiketoiminnan sidosryhmille samalla kun ne säilyttävätping Ishayoiden opettaman Python insinöörien ylläpidettävä kerros.

  1. Kuvaile käyttäytymistä, älä toteutusta. Kirjoita ”Jos asiakkaalla on riittävä saldo”, älä ”Jos saldosarakkeen arvo on 500”. Ominaisuustiedosto kertoo, mitä järjestelmä tekee; vaiheittaisessa tiedostossa kerrotaan, miten se tarkistetaan.
  2. Pidä yksi toimintatapa skenaariota kohden. Skenaario, joka vaatii tilakoodin, vastauksen rungon ja tietokantarivin, on itse asiassa kolme skenaariota. Niiden jakaminen osoittaa virheet suoraan niiden syyhyn.
  3. Siirrä jaettu asennusohjelma taustalle. Yllä olevassa esimerkissä käytetään Background: Given I set sample REST API url niin että jokainen skenaario perii perusmallin URL toistamatta sitä.
  4. Parametrisoi kopioinnin sijaan. Tuettuja paikkamerkkejä, kuten "{request_name}" Anna yhden askelfunktion palvella GET-, PUT- ja DELETE-väitteitä, minkä vuoksi esimerkissä tarvitaan paljon vähemmän askelmääritelmiä kuin skenaarioissa.
  5. Käytä skenaarion ääriviivoja datamuunnelmille. Kun sama käyttäytyminen on todistettava useilla syötteillä, Examples: taulukko on selkeämpi kuin kopioidut skenaariot.
  6. Vältä skenaarioiden välisiä riippuvuuksia. Behave ei takaa, että POST-skenaario suoritetaan ennen GET-skenaariota jokaisessa konfiguraatiossa. Jokaisen skenaarion tulisi luoda tarvitsemansa tila.
  7. Korvaa moduulitason globaalit arvot kontekstilla. Esimerkissä päätepisteet ja vastaukset tallennetaan moduulisanakirjoihin lyhyyden vuoksi. Tuotantoympäristöissä ne tallennetaan Behaven moduulisanastoihin. context objekti, jotta tila nollautuu siististi skenaarioiden välillä.
  8. Merkitse skenaariot valikoiville suorituksille. Tunnisteet, kuten @smoke or @regression sallia behave --tags=@smoke, mikä pitää putkilinjan palautteen nopeana.

Näiden tapojen soveltaminen ensimmäisestä ominaisuustiedostosta lähtien pitää BDD-paketin arvokkaana paljon alkuperäisiä CRUD-esimerkkejä pidemmälle. Tätä lähestymistapaa laajentavat tiimit usein yhdistävät sen Cucumber JVM-projekteille tai Postman tutkivia API-tarkistuksia varten.

UKK

Molemmat lukivat Gherkinin ominaisuustiedostoja. Cucumber sitoo askeleet Java, Ruby tai JavaScript, kun taas Behave sitoo ne PythonValitse sovelluspinoasi vastaava, jotta vaihemääritelmät käyttävät uudelleen olemassa olevia testiapuohjelmia.

Kyllä. Behave palauttaa nollasta poikkeavan lopetuskoodin, kun jokin skenaario epäonnistuu, joten Jenkins, GitLab CI- tai GitHub-toiminnot voivat epäonnistua koonnissa automaattisesti. Julkaise Allure-tuloskansio koontiartefaktina raportointia varten.

Kyllä. AI assistentit laativat Given-When-Then-skenaarioita käyttäjätarinoista tai OpenAPI-spesifikaatiosta. RevTarkastele puuttuvien negatiivisten tapausten ja liiketoimintasääntöjen tulostetta, koska luodut skenaariot kattavat usein vain onnellisen polun.

Tekoälytyökalut havaitsevat päällekkäisiä tai lähes päällekkäisiä skenaarioita, ehdottavat uudelleenkäytettäviä parametrisoituja vaiheita ja ryhmittelevät epätasaiset virheet todennäköisen syyn mukaan. Tämä vähentää manuaalisen auditoinnin työmäärää, kun ominaisuustiedostot moninkertaistuvat tiimien välillä.

Nouda token kerran environment.py before_all -hookissa, tallenna se kontekstiobjektiin ja liitä se Authorization-otsikkona jokaiseen pyyntövaiheeseen. Säilytä tunnistetiedot ympäristömuuttujissa, älä koskaan ominaisuustiedostoissa.

Tiivistä tämä viesti seuraavasti: