Какво е BDD тестване? Рамка за поведенческо ориентирано разработване
⚡ Умно обобщение
BDD тестването описва поведението на приложението на разбираем език „Дадено-Когато-Тогава“, така че анализатори, разработчици и тестери споделят една спецификация. Това ръководство прилага този подход към REST API тестването с Behave, Python рамка, обхващаща настройка, файлове с функции, внедряване на стъпки, изпълнение и отчитане.

Какво е BDD (развитие, управлявано от поведение) тестване?
Тестване на BDD (поведенческо развитие). е техника за гъвкаво разработване на софтуер и е разширение на TDD, т.е. Разработка, управлявана от тестове. В BDD тестовите случаи се пишат на естествен език, който дори непрограмисти могат да четат.
Как работи BDD тестването?
Да предположим, че ви е възложена задачата да създадете модул за превод на средства в приложение за Net Banking.
Има няколко начина да го тествате:
- Преводът на средства трябва да се осъществи, ако има достатъчно салдо в сметката източник
- Преводът на средства трябва да се осъществи, ако данните за дестинацията са правилни
- Преводът на средства трябва да се осъществи, ако паролата за транзакцията / RSA кодът / удостоверяването за сигурност, въведени от потребителя, са правилни.
- Преводът на средства трябва да се извърши дори ако е официален празник
- Преводът на средства трябва да се извърши на бъдеща дата, определена от титуляра на сметката
- Сценарий на теста става по-сложно и комплексно, когато вземем предвид допълнителни функции, като например прехвърляне на сума X за интервал от Y дни или месеца, спиранеping планиран превод, когато общата сума достигне Z и така нататък.
Общата тенденция на разработчиците е да разработват функции и да пишат тестов код по-късно. Както е видно от горния случай, Тестов случай Разработката тук е сложна, така че разработчикът ще отложи Тестване до пускането му на пазара, като в този момент се извършват бързи, но неефективни тестове.
За да се преодолее този проблем, беше създадена Behavior-Driven Development (BDD). Тя улеснява целия процес на тестване за разработчика.
В BDD каквото напишеш трябва да влезе Даден-кога-тогава стъпки. Нека разгледаме същия пример по-горе в 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
Този формуляр е лесен за писане, четене и разбиране. Той обхваща всеки възможен тестов случай за модула за прехвърляне на средства и може бързо да бъде модифициран, за да се вмести в повече модули. Той също така се чете като „жива“ документация за модула. Тъй като BDD произлиза от TDD, разликата между двете си струва да се разбере, преди да се премине към инструментариума.
BDD срещу TDD: Ключови разлики
И двете практики са насочени към тестване първо и съкращават циклите на обратна връзка. Те се различават по това кой пише тестовете, какъв език използват тези тестове и кой слой от приложението описват.
| Аспект | TDD (Разработка, управлявана от тестове) | BDD (поведенческо развитие) |
|---|---|---|
| Фокус | Как работи вътрешно една единица код | Как се държи системата от гледна точка на потребителя |
| Език | Твърдения в езика за програмиране | Сценарии „Дадено-Когато-Тогава“ на естествен език |
| Основен автор | Софтуерен Инженер | Бизнес анализатор, собственик на продукт, тестер и разработчик заедно |
| Readable от непрограмисти | Не | Да |
| Типичен обхват | Ниво на единица | Функция, API и ниво на приемане |
| Често срещани инструменти | JUnit, pytest, NUnit | Дръж се добре, Cucumber, SpecFlow, JBehave |
Двете са по-скоро допълващи се, отколкото конкуриращи се. Екипите обикновено поддържат TDD за проектиране на ниво единица и добавете BDD сценарии отгоре, за да опишете поведението, което заинтересованите страни действително одобряват. Тъй като REST крайните точки се намират точно на този слой за приемане, те са естествено решение за BDD.
Какво е REST API тестване?
Тъй като REST се превърна в популярен стил за изграждане на API, автоматизирането на REST API тестови случаи, наред с UI тестове, стана също толкова важно. REST API тестване включва тестване на CRUD (Create-Read-Update-Delete) действия съответно с методите POST, GET, PUT и DELETE.
Какво е Behave?
Behave е един от популярните Python BDD тестови рамки. Ето как функционира Behave:
- Файловете с характеристики се пишат от вашия бизнес анализатор, спонсор или който и да е собственик на сценариите за поведение. Файлът с характеристики използва формат на естествен език, описващ характеристика или част от характеристика, с представителни примери за очаквани резултати.
- Тези стъпки от сценария са съпоставени с имплементации на стъпки, написани на Python.
- По избор, контролите на средата изпълняват код преди и след стъпки, сценарии, функции или цялото изпълнение.
След като ролите на всеки файл са ясни, рамката вече може да бъде инсталирана.
Настройване на BDD тестова рамка Behave on Windows
Монтаж:
- Изтеглете и инсталирайте Python 3 от https://www.python.org/
- Изпълнете следната команда в командния ред, за да инсталирате Behave
pip install behave- SDI: PyCharm Тук се използва Community Edition — https://www.jetbrains.com/pycharm/download/
Настройка на проекта:
- Създайте нов проект
- Създайте следната структура на директории
Снимката на екрана по-горе показва оформлението, което Behave очаква: a Характеристика директория, съдържаща файловете с функции, и вложена стъпки директорията, съдържаща Python реализации. Behave открива и двете по име, така че имената на папките трябва да съвпадат точно.
Файлове с функции:
Сега нека изградим файла с характеристиките Sample_REST_API_Testing.feature, като функцията е CRUD операции в услугата „публикации“.
Този пример използва https://jsonplaceholder.typicode.com/ примерна REST услуга за публикации, безплатен фалшив API, който приема заявки за запис и връща реалистични отговори без запазване на промените.
Примерен 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
По подобен начин можете да напишете останалите сценарии, както следва:
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"
Стъпки Изпълнение
Сега, за стъпките на функциите, използвани в горните сценарии, можете да напишете имплементации в Python файлове в директорията „steps“.
Рамката Behave идентифицира функцията step чрез съпоставяне на декоратори с предиката на файла с характеристики. Например, даден Предикатът в сценарий с файл с характеристики търси стъпкова функция, носеща @given декоратор. Същото съвпадение се случва и за Кога намлява След товаВ случая на „But“ и „And“, функцията step приема същия декоратор като предходната си стъпка. Например, ако „And“ следва дадено условие, съответстващият декоратор на функцията step е @given.
Например, стъпката When за POST може да бъде реализирана по следния начин. Обърнете внимание как "application/json" се предава от файла с характеристики в заградения в скоби заместител — това се нарича параметризация.
# 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
По подобен начин, изпълнението на другите стъпки в стъпката Python файлът ще изглежда така:
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
⚠️ Забележка: Сравняване на кодове за състояние с == , а не is има значение. is операторът проверява идентичността на обекта, а не равенството, и Python 3.8 и по-нови версии повишават Синтаксично предупреждение: „е“ с литерал за този модел. Всяко твърдение, записано като assert code is 201 трябва да се пренапише като assert code == 201.
Изпълнение на тестовете
Разработването на тестовия скрипт е завършено, така че нека стартираме тестовете. Изпълнете следната команда в командния ред, за да стартирате файла с функциите:
:: 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
Това показва резултатите от изпълнението на теста, както следва:
Показване на отчет на конзолата
Изходът от конзолата е удобен по време на разработка, но заинтересованите страни обикновено предпочитат четлив отчет, който Allure предоставя.
Доклади
Първо, инсталирайте форматиращия инструмент Allure Behave и инструмента за команден ред Allure. Форматиращият инструмент е... Python пакет; инструментът за команден ред е отделно изтегляне, описано в Документация на Allure Report.
:: 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
Това генерира отчета с резултатите от теста в представителен и информативен формат, подобен на този:
Доклад от теста в HTML формат
Доклад от теста, показващ индивидуален резултат от сценария
Работещият пакет е полезен само ако остава четим с нарастването на API-то, което е мястото, където дисциплината на файловете с функции се отплаща.
Най-добри практики за писане на Behave Feature файлове
Пакетът Behave се деградира бързо, когато сценариите описват кликвания и полезни товари, вместо поведение. Практиките по-долу поддържат файловете с функции четливи за заинтересованите страни в бизнеса, като същевременно се запазва...ping - Python слой, поддържан от инженери.
- Описвайте поведението, а не имплементацията. Напишете „Ако клиентът има достатъчно салдо“, а не „Ако колоната с салдо е равна на 500“. Файлът с функции посочва какво прави системата; файлът със стъпки посочва как се проверява.
- Запазете едно поведение за всеки сценарий. Сценарий, който посочва код на състоянието, тяло на отговора и ред от базата данни, всъщност представлява три сценария. Разделянето им кара грешките да сочат директно към причината.
- Преместете споделената настройка във фонов режим. В горния пример се използва
Background: Given I set sample REST API urlтака че всеки сценарий наследява базата URL без да го повтарям. - Параметризирайте вместо да дублирате. Закрепени заместители, като например
"{request_name}"Функцията Let one step обслужва GET, PUT и DELETE твърдения, поради което примерът се нуждае от много по-малко дефиниции на стъпки, отколкото сценариите. - Използвайте схемата на сценария за вариации в данните. Когато едно и също поведение трябва да се докаже за няколко входни данни,
Examples:таблицата е по-ясна от копираните сценарии. - Избягвайте зависимости между сценарии. Behave не гарантира, че POST сценарий ще се изпълнява преди GET сценарий във всяка конфигурация. Всеки сценарий трябва да създаде необходимото му състояние.
- Заменете глобалните променливи на ниво модул с контекстни. Примерът съхранява крайните точки и отговорите в речници на модули за краткост. В производствените пакети ги съхранявайте в Behave-овите
contextобект, така че състоянието да се нулира чисто между сценариите. - Маркирайте сценарии за селективни изпълнения. Етикети като
@smokeor@regressionпозволиbehave --tags=@smoke, което поддържа обратната връзка от тръбопровода бърза.
Прилагането на тези навици от първия файл с функции поддържа BDD пакета ценен далеч отвъд първоначалните CRUD примери. Екипите, които разширяват този подход допълнително, често го съчетават с Cucumber за JVM проекти или Postman за проучвателни API проверки.






