Что такое BDD-тестирование? Фреймворк разработки, основанный на поведении.
⚡ Умное резюме
BDD-тестирование описывает поведение приложения простым языком, основанным на принципе «Дано, когда, тогда», что позволяет аналитикам, разработчикам и тестировщикам использовать единую спецификацию. В этом пошаговом руководстве этот подход применяется к тестированию REST API с помощью Behave. Python Структура проекта, включающая настройку, файлы функций, реализацию шагов, выполнение и составление отчетов.

Что такое BDD-тестирование (разработка, основанная на поведении)?
BDD (Разработка, основанная на поведении) Тестирование BDD — это метод гибкой разработки программного обеспечения, являющийся расширением TDD, то есть разработки через тестирование. В BDD тестовые примеры пишутся на естественном языке, который могут прочитать даже непрограммисты.
Как работает BDD-тестирование?
Предположим, вам поручено создать модуль для перевода средств в приложении интернет-банкинга.
Существует несколько способов это проверить:
- Перевод средств должен быть осуществлен, если на исходном счете достаточно средств.
- Перевод средств должен быть осуществлен, если данные о пункте назначения верны.
- Перевод средств должен состояться, если введенный пользователем пароль транзакции / код 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 | Вести себя, CucumberSpecFlow, JBehave |
Эти два понятия дополняют друг друга, а не конкурируют. Команды обычно сохраняют TDD Для проектирования на уровне модулей можно добавить сценарии BDD, описывающие поведение, которое фактически утверждают заинтересованные стороны. Поскольку REST-конечные точки находятся именно на этом уровне приемки, они идеально подходят для BDD.
Что такое тестирование REST API?
Поскольку REST стал популярным стилем построения API, автоматизация тестовых сценариев для REST API наряду с тестовыми сценариями для пользовательского интерфейса стала одинаково важной. REST API тестирование Это включает тестирование операций CRUD (создание-чтение-обновление-удаление) с использованием методов POST, GET, PUT и DELETE соответственно.
Что такое поведение?
Поведение – одно из популярных Python Тестовые фреймворки BDD. Вот как работает Behave:
- Файлы описания функций создаются вашим бизнес-аналитиком, спонсором или тем, кто отвечает за сценарии поведения. В файле описания функции используется формат естественного языка, описывающий функцию или часть функции, с примерами ожидаемых результатов.
- Эти этапы сценария сопоставляются с реализациями этапов, написанными на Python.
- При желании, средства управления средой могут запускать код до и после шагов, сценариев, функций или на протяжении всего выполнения программы.
После того, как роли каждого файла определены, можно приступать к установке фреймворка.
Настройка платформы тестирования BDD Windows
Установка:
- Скачайте и установите Python 3 из https://www.python.org/
- Выполните следующую команду в командной строке, чтобы установить Behave.
pip install behave- ИДЕ: PyCharm Здесь используется Community Edition — https://www.jetbrains.com/pycharm/download/
Настройка проекта:
- Создание нового проекта
- Создайте следующую структуру каталогов.
На скриншоте выше показана структура, которую ожидает Behave: a функции каталог, содержащий файлы функций, и вложенный шага каталог, содержащий Python реализации. Behave обнаруживает оба варианта по имени, поэтому имена папок должны точно совпадать.
Функциональные файлы:
Теперь давайте создадим файл с описанием функций. Sample_REST_API_Testing.feature, при этом особенностью являются операции CRUD (операции чтения-выполнения) над сервисом 'posts'.
В этом примере используется https://jsonplaceholder.typicode.com/ Сервис posts sample 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 определяет функцию шага, сопоставляя декораторы с предикатом файла объектов. Например, Данный Предикат в сценарии файла функций ищет ступенчатую функцию, несущую @given декоратор. Аналогичное сочетание происходит и для После появления и ЗатемВ случае операторов 'But' и 'And' функция шага использует тот же декоратор, что и предыдущий шаг. Например, если 'And' следует за оператором Given, то соответствующий декоратор функции шага — это @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 и последующие события вызывают SyntaxWarning: “is” with a literal для этого шаблона. Любое утверждение, записанное как 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.
:: 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
Эффективность набора инструментов Behave быстро снижается, когда сценарии описывают клики и полезную нагрузку вместо поведения. Приведенные ниже методы позволяют сохранять читаемость файлов с описанием функций для заинтересованных сторон из бизнеса, одновременно обеспечивая...ping Python Этот слой удобен для сопровождения инженерами.
- Описывайте поведение, а не его реализацию. Пишите «При условии, что у клиента достаточный баланс», а не «При условии, что значение в столбце «Баланс» равно 500». В файле описания функций указано, что делает система; в файле описания шага указано, как это проверяется.
- Для каждого сценария используйте одно действие. Сценарий, в котором указывается код состояния, тело ответа и строка в базе данных, на самом деле представляет собой три сценария. Разделение их позволяет напрямую указывать на причину сбоев.
- Перевести общие настройки в фоновый режим. В приведенном выше примере используется
Background: Given I set sample REST API urlтак что каждый сценарий наследует базовый. URL не повторяя его. - Вместо дублирования используйте параметры. Заполнители с скобками, такие как
"{request_name}"Пусть одна функция шага обслуживает утверждения GET, PUT и DELETE, поэтому в примере требуется гораздо меньше определений шагов, чем в сценариях. - Используйте структуру сценария для отображения различных вариантов данных. Когда необходимо доказать одинаковое поведение для нескольких входных данных,
Examples:Таблица понятнее, чем скопированные сценарии. - Избегайте зависимостей между сценариями. Behave не гарантирует, что сценарий POST будет выполняться раньше сценария GET в каждой конфигурации. Каждый сценарий должен создавать необходимое ему состояние.
- Замените глобальные переменные на уровне модуля на контекст. В приведенном примере для краткости конечные точки и ответы хранятся в словарях модулей. В производственных средах их следует хранить в Behave.
contextТаким образом, состояние объекта корректно сбрасывается между сценариями. - Пометьте сценарии для выборочных запусков. Теги, такие как
@smokeor@regressionпозволятьbehave --tags=@smokeчто обеспечивает быструю обратную связь в процессе обработки данных.
Применение этих принципов с самого первого файла описания функций позволяет сохранить ценность набора инструментов BDD далеко за пределами первоначальных примеров CRUD. Команды, расширяющие этот подход, часто сочетают его с Cucumber для проектов на JVM или Postman для ознакомительных проверок API.






