Какво е BDD тестване? Рамка за поведенческо ориентирано разработване

⚡ Умно обобщение

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

  • 🗣️ Основен принцип: Разработката, управлявана от поведението, разширява разработката, управлявана от тестове, като изразява всеки сценарий на естествен език, който непрограмистите могат да прочетат и одобрят.
  • 🧱 Структура на сценария: Given установява предварителните условия, When извършва действието и Then потвърждава резултата, докато And или But наследяват декоратора на предходната стъпка.
  • 🌐 Покритие на REST: Примерът упражнява пълно CRUD поведение чрез POST, GET, PUT и DELETE спрямо публичната услуга за публикации jsonplaceholder.
  • 🐍 Настройка на рамката: Behave се инсталира чрез pip на Python 3 и очаква директория с функции, съдържаща файлове с функции, плюс пакет със стъпки, съдържащ имплементациите.
  • 🔗 Стъпка за свързване: Декоратори като @given, @when и @then съответстват на текста на функцията, а заместителите в скоби предават стойности от сценария в Python функция.
  • ▶ ️ Екзекуция: Красивият формататор отпечатва цветно кодиран резултат за преминаване или неуспешно преминаване за всяка стъпка в конзолата.
  • 📊 Отчитане: Форматиращият модул allure-behave записва машинно четими резултати, които командният ред на Allure рендира като HTML отчет, който може да се разглежда.

Какво е BDD тестване

Какво е BDD (развитие, управлявано от поведение) тестване?

Тестване на BDD (поведенческо развитие). е техника за гъвкаво разработване на софтуер и е разширение на TDD, т.е. Разработка, управлявана от тестове. В BDD тестовите случаи се пишат на естествен език, който дори непрограмисти могат да четат.

Как работи BDD тестването?

Да предположим, че ви е възложена задачата да създадете модул за превод на средства в приложение за Net Banking.

Има няколко начина да го тествате:

  1. Преводът на средства трябва да се осъществи, ако има достатъчно салдо в сметката източник
  2. Преводът на средства трябва да се осъществи, ако данните за дестинацията са правилни
  3. Преводът на средства трябва да се осъществи, ако паролата за транзакцията / RSA кодът / удостоверяването за сигурност, въведени от потребителя, са правилни.
  4. Преводът на средства трябва да се извърши дори ако е официален празник
  5. Преводът на средства трябва да се извърши на бъдеща дата, определена от титуляра на сметката

- Сценарий на теста става по-сложно и комплексно, когато вземем предвид допълнителни функции, като например прехвърляне на сума 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 формат

Доклад от теста в HTML формат

Доклад от теста, показващ индивидуален резултат от сценария

Работещият пакет е полезен само ако остава четим с нарастването на API-то, което е мястото, където дисциплината на файловете с функции се отплаща.

Най-добри практики за писане на Behave Feature файлове

Пакетът Behave се деградира бързо, когато сценариите описват кликвания и полезни товари, вместо поведение. Практиките по-долу поддържат файловете с функции четливи за заинтересованите страни в бизнеса, като същевременно се запазва...ping - Python слой, поддържан от инженери.

  1. Описвайте поведението, а не имплементацията. Напишете „Ако клиентът има достатъчно салдо“, а не „Ако колоната с салдо е равна на 500“. Файлът с функции посочва какво прави системата; файлът със стъпки посочва как се проверява.
  2. Запазете едно поведение за всеки сценарий. Сценарий, който посочва код на състоянието, тяло на отговора и ред от базата данни, всъщност представлява три сценария. Разделянето им кара грешките да сочат директно към причината.
  3. Преместете споделената настройка във фонов режим. В горния пример се използва Background: Given I set sample REST API url така че всеки сценарий наследява базата URL без да го повтарям.
  4. Параметризирайте вместо да дублирате. Закрепени заместители, като например "{request_name}" Функцията Let one step обслужва GET, PUT и DELETE твърдения, поради което примерът се нуждае от много по-малко дефиниции на стъпки, отколкото сценариите.
  5. Използвайте схемата на сценария за вариации в данните. Когато едно и също поведение трябва да се докаже за няколко входни данни, Examples: таблицата е по-ясна от копираните сценарии.
  6. Избягвайте зависимости между сценарии. Behave не гарантира, че POST сценарий ще се изпълнява преди GET сценарий във всяка конфигурация. Всеки сценарий трябва да създаде необходимото му състояние.
  7. Заменете глобалните променливи на ниво модул с контекстни. Примерът съхранява крайните точки и отговорите в речници на модули за краткост. В производствените пакети ги съхранявайте в Behave-овите context обект, така че състоянието да се нулира чисто между сценариите.
  8. Маркирайте сценарии за селективни изпълнения. Етикети като @smoke or @regression позволи behave --tags=@smoke, което поддържа обратната връзка от тръбопровода бърза.

Прилагането на тези навици от първия файл с функции поддържа BDD пакета ценен далеч отвъд първоначалните CRUD примери. Екипите, които разширяват този подход допълнително, често го съчетават с Cucumber за JVM проекти или Postman за проучвателни API проверки.

Въпроси и Отговори

И двете четат файлове с функции на Gherkin. Cucumber свързва стъпките с Java, Руби или JavaСкрипт, докато Behave ги обвързва с PythonИзберете този, който съответства на вашия стек от приложения, така че дефинициите на стъпките да използват повторно съществуващите помощни програми за тестване.

Да. Behave връща ненулев код за изход, когато някой сценарий се провали, така че Jenkins, GitLab CI или GitHub Actions могат автоматично да доведат до неуспех на компилацията. Публикувайте папката с резултати от Allure като артефакт за компилация за отчитане.

Да. AI Асистентите изготвят сценарии „Дадено-Когато-Тогава“ от потребителски истории или спецификация на OpenAPI. RevВижте резултата за липсващи отрицателни случаи и бизнес правила, тъй като генерираните сценарии често покриват само щастливия път.

Инструментите с изкуствен интелект откриват дублирани или почти дублирани сценарии, предлагат повторно използваеми параметризирани стъпки и клъстерират нестабилни повреди по вероятна причина. Това намалява усилията за ръчен одит, тъй като файловете с функции се умножават между екипите.

Извлечете токена веднъж в `environment.py before_all` hook-а, съхранете го в контекстния обект и го прикачете като заглавка за оторизация във всяка стъпка на заявката. Съхранявайте идентификационните данни в променливите на средата, никога във файловете с функции.

Обобщете тази публикация с: