BDD — разработка через поведение. BDD для микросервисов — это сотрудничество клиента, разработчиков и тестировщиков. BDD — это разработка, которая учитывает и технические интересы и бизнес-требования. Этот подход обычно применяется для описания интерфейсов приложений, а так как микросервисы — детали реализации системы, то BDD прекрасно походит и для разработки микросервисов. Как это сделать — в переводе материала Ken Pugh.
Об авторе: Ken Pugh обучает компании развивать гибкость, создает высококачественные системы с помощью Acceptance Test-Driven Development, BDD, ускорения DevOps. Кен написал несколько книг по разработке ПО, был лауреатом премии Jolt Award 2006 Prefactoring, один из создателей курса SAFe Agile Software Engineering.
К примеру, stateless логика, такая как бизнес-правила и вычисления, просто описывает преобразование входных данных к выходным.
Интерфейсно-ориентированный дизайн — Interface Oriented Design, использует принцип «проектирования относительно интерфейсов, а не реализаций». Потребители сервиса используют интерфейс, который он предоставляет, а не внутренности. Это значит, что такой интерфейс должен быть четко продуман, включая поведение при ошибках. Для определения терминов в описании интерфейса или его поведения можно использовать DDD — Domain Driven Design.
Микросервисы могут быть синхронными, когда потребитель напрямую вызывает другой сервис и ожидает результата, либо асинхронными, когда сервис отвечает на сообщение, которое клиент поместил в очередь.
Рассмотрим пример синхронного сервиса.
Представим сервис, который вычисляет скидку на заказ клиента.Весь процесс — набор связанных операций.
Поведение этого сервиса можно описать так:
Сервис может вычислить скидку используя алгоритмы в коде, на основании локальной базы данных данных или связавшись с другими сервисами.
Он может использовать JSON или XML в качестве формата сообщений. Однако, описание сервиса без указания деталей реализации помогает отделить семантику операций от синтаксиса.
Используя BDD, можно начать проектирование с примера данных, чтобы получить представление о требуемом поведении. Разработчики сервиса, клиента и тестировщик могут прийти к такому примеру. Первые два столбца — входные данные для сервиса, а столбец справа — выходные.
Пример показывает доменные термины, которым может потребоваться дальнейшее уточнение — например, описать допустимые значения.
Подразумевается, что сервис возвращает правильный результат, если входные данные попадают в диапазон допустимых значений.
Описание поведения, особенно для микросервисов, часто включает ответы в случае сбоев и ошибок. Описание потенциальных сбоев помогает потребителю понять, что ему делать в таких случаях. Клиенты сервиса могут использовать специальные библиотеки, например, Hystrix от Netflix, для устранения некоторых из этих сбоев.
Некоторые потенциальные ошибки нашего сервиса:
Сбои могут быть выражены в виде числовых или символьных констант в протоколе связи.
Если значения, которое передали в качестве категории, нет в списке допустимых значений, то сервис вернет индикатор сбоя: «Неверное значение параметров запроса». Это может быть представлено, например, возвратом HTTP c кодом 400 и соответствующим текстовым описанием.
Альтернативно, можно определить значение скидки, которое будет возвращаться, например, 0, если какой-либо из параметров неверен. Сервис в таком случае должен нести ответственность за регистрацию этой проблемы, чтобы можно было проанализировать ее последствия.
BDD-тесты сервиса могут формировать контекст для его модульных тестов. В процессе проектирования ответственность за прохождение тестов BDD возлагается на классы и методы. Модульные тесты определяют эти обязанности.
При тестировании сервиса часто требуются заглушки зависимых сервисов, которые он вызывает. Они особенно нужны для медленных, дорогих или случайных сервисов. Если поведение сервиса скидок никогда не менялось, то при тестировании клиента можно использовать боевой инстанс.
Заглушка может всегда отдавать одни и те же значения, например:
Тесты клиента могут опираться на эти значения. В этом примере постоянного поведения может быть достаточно. Для других тестов предпочтительнее настраиваемый ответ заглушки.
В качестве альтернативы, заглушка сервиса скидок может просто возвращать одну и ту же сумму, независимо от входных данных.
Как эта заглушка может вписаться в более широкий сценарий? Рассмотрим поведение системы для заказа, который включает в себя как скидку, так и налог. Налог рассчитывается микросервисом, аналогичным скидке.
Есть покупатель.
Настраивается скидка.
Устанавливается налог.
Когда клиент размещает заказ:
Тогда параметры заказа.
Если сервис скидок использует БД, чтобы получить информацию для расчета скидки, то ее содержимое — это состояние сервиса. Изменения состояния в ответ на обновления данных, должно быть задокументировано. Предположим, что сервис имел такое состояние.
В таком случае, сервис должен позволять менять эти данные. Обновление может быть организовано так, чтобы обновляемыми были отдельные элементы или чтобы обновлялась вся таблица сразу. Вот пример поведенческого теста для индивидуального обновления.
Даны текущие данные.
Когда элемент обновляется.
Тогда обновленные данные.
Можно также проверить, что обновленные данные используются для расчета скидки.
У сервиса скидок может быть локальное хранилище для сохранения данных в этом примере, но он может зависеть и от отдельного сервиса хранения этих данных. Если так, то тесты из предыдущего раздела применяются и к отдельному сервису. Но каждая зависимость добавляет проблемы. Каким должно быть поведение службы, если ее зависимости недоступны? Для сервиса скидок это должно указывать на сбой или он должен просто возвращать значение по-умолчанию, тот же 0? Иногда можно использовать глобальные политики обработки ошибок, но часто решение зависит от контекста сервиса.
После того, как поведение микросервиса согласовано, его можно сформулировать в виде автоматизированных тестов. Существует несколько систем тестирования микросервисов, таких как PACT или Karate. Кроме того, вы можете использовать BDD-фреймворки, такие как Cucumber или FIT.
Например Cucumber, использует библиотеки для запросов к сервисам. Тогда дополнительная информация об окружении может быть представлена ??как часть сценария.
Например, файл объектов Cucumber может включать.
Варианты шагов зависят от ваших соглашений тестирования.
Значения в первых двух столбцах могут быть перенесены в любое соглашение о вызовах, например, в параметры запроса. Результат в body должен соответствовать третьему столбцу. Если имена и значения запроса — это имена и значения столбцов, это уменьшает различия между тестом и реализацией.
Для повторного использования шаги могут быть написаны для произвольного сервиса, который делает вычисления или определяет результат выполнения бизнес-правила. В приведенном выше примере использование символа «?», как в «Discount Amount» выше, помогает анализатору различать входные и выходные данные.
Тесты также должны включать проверки на отказы, например.
Микросервисы зависят от других сервисов и систем, что требует четкой спецификации интерфейсов и их аккуратное тестирование. Этого можно добиться с помощью описания поведения и интерфейсов, заданных с помощью тестов. С помощью BDD, функциональность сервисов описывается исполняемыми тестами, которые фокусируются на семантике операций, а не на синтаксисе. Автоматизация таких тестов обычно требует настройки заглушек других сервисов, поведение которых описываются их отдельными BDD-тестами.
Интерфейсно-ориентированное проектирование — IOD, включает дополнительные обязательства сервиса: ограничение на использование ресурсов, пропускная способность и отчеты об ошибках. Вместе BDD и IOD помогают описать поведение сервиса, чтобы клиенты могли легко понять и положиться на него.
Об авторе: Ken Pugh обучает компании развивать гибкость, создает высококачественные системы с помощью Acceptance Test-Driven Development, BDD, ускорения DevOps. Кен написал несколько книг по разработке ПО, был лауреатом премии Jolt Award 2006 Prefactoring, один из создателей курса SAFe Agile Software Engineering.
Поведение в BDD часто выражается конструкцией Given/When/Then. Нам дано определенное состояние, когда происходит действие или событие, тогда состояние изменяется и/или возвращается информация.
К примеру, stateless логика, такая как бизнес-правила и вычисления, просто описывает преобразование входных данных к выходным.
Интерфейсно-ориентированный дизайн — Interface Oriented Design, использует принцип «проектирования относительно интерфейсов, а не реализаций». Потребители сервиса используют интерфейс, который он предоставляет, а не внутренности. Это значит, что такой интерфейс должен быть четко продуман, включая поведение при ошибках. Для определения терминов в описании интерфейса или его поведения можно использовать DDD — Domain Driven Design.
Микросервисы могут быть синхронными, когда потребитель напрямую вызывает другой сервис и ожидает результата, либо асинхронными, когда сервис отвечает на сообщение, которое клиент поместил в очередь.
Рассмотрим пример синхронного сервиса.
Синхронный сервис
Представим сервис, который вычисляет скидку на заказ клиента.Весь процесс — набор связанных операций.
Поведение этого сервиса можно описать так:
Get discount for a customer
Given these inputs
Customer category
Order Amount
Then service outputs
Discount Amount
Сервис может вычислить скидку используя алгоритмы в коде, на основании локальной базы данных данных или связавшись с другими сервисами.
Он может использовать JSON или XML в качестве формата сообщений. Однако, описание сервиса без указания деталей реализации помогает отделить семантику операций от синтаксиса.
Что такое поведение?
Используя BDD, можно начать проектирование с примера данных, чтобы получить представление о требуемом поведении. Разработчики сервиса, клиента и тестировщик могут прийти к такому примеру. Первые два столбца — входные данные для сервиса, а столбец справа — выходные.
Категория клиентов |
Сумма заказа |
Сумма скидки? |
Good |
100,00 USD |
1,00 USD |
Excellent |
100,00 USD |
2,00 USD |
Пример показывает доменные термины, которым может потребоваться дальнейшее уточнение — например, описать допустимые значения.
Подразумевается, что сервис возвращает правильный результат, если входные данные попадают в диапазон допустимых значений.
Описание поведения, особенно для микросервисов, часто включает ответы в случае сбоев и ошибок. Описание потенциальных сбоев помогает потребителю понять, что ему делать в таких случаях. Клиенты сервиса могут использовать специальные библиотеки, например, Hystrix от Netflix, для устранения некоторых из этих сбоев.
Некоторые потенциальные ошибки нашего сервиса:
Сбой |
Неверный синтаксис запроса |
Таймаут вызова зависимых сервисов |
Неверное значение параметров запроса |
Сбои могут быть выражены в виде числовых или символьных констант в протоколе связи.
Использование значимых имен в BDD помогает подчеркнуть семантику сбоя, а не его синтаксис.
Если значения, которое передали в качестве категории, нет в списке допустимых значений, то сервис вернет индикатор сбоя: «Неверное значение параметров запроса». Это может быть представлено, например, возвратом HTTP c кодом 400 и соответствующим текстовым описанием.
Альтернативно, можно определить значение скидки, которое будет возвращаться, например, 0, если какой-либо из параметров неверен. Сервис в таком случае должен нести ответственность за регистрацию этой проблемы, чтобы можно было проанализировать ее последствия.
BDD-тесты сервиса могут формировать контекст для его модульных тестов. В процессе проектирования ответственность за прохождение тестов BDD возлагается на классы и методы. Модульные тесты определяют эти обязанности.
Заглушки
При тестировании сервиса часто требуются заглушки зависимых сервисов, которые он вызывает. Они особенно нужны для медленных, дорогих или случайных сервисов. Если поведение сервиса скидок никогда не менялось, то при тестировании клиента можно использовать боевой инстанс.
Изменения часто неизбежны, поэтому обычно заглушки нужны.
Заглушка может всегда отдавать одни и те же значения, например:
Категория клиентов |
Сумма заказа |
Сумма скидки? |
Good |
100,00 USD |
1,00 USD |
Excellent |
100,00 USD |
2,00 USD |
Тесты клиента могут опираться на эти значения. В этом примере постоянного поведения может быть достаточно. Для других тестов предпочтительнее настраиваемый ответ заглушки.
В качестве альтернативы, заглушка сервиса скидок может просто возвращать одну и ту же сумму, независимо от входных данных.
Как эта заглушка может вписаться в более широкий сценарий? Рассмотрим поведение системы для заказа, который включает в себя как скидку, так и налог. Налог рассчитывается микросервисом, аналогичным скидке.
Есть покупатель.
Категория покупателя |
Локация |
Good |
Северная Каролина |
Настраивается скидка.
Customer Category |
Order Amount |
Discount Amount? |
Good |
100,00 USD |
1,00 USD |
Устанавливается налог.
Место нахождения |
Количество |
Налог? |
Северная Каролина |
99,00 USD |
6,60 USD |
Когда клиент размещает заказ:
Сумма заказа |
100,00 USD |
Тогда параметры заказа.
Сумма заказа |
скидка |
Сумма после скидки |
налог |
Итого к оплате |
100 USD |
1,00 USD |
99,00 USD |
6,60 USD |
105,60 USD |
Сервисы с состоянием
Если сервис скидок использует БД, чтобы получить информацию для расчета скидки, то ее содержимое — это состояние сервиса. Изменения состояния в ответ на обновления данных, должно быть задокументировано. Предположим, что сервис имел такое состояние.
Категория клиентов |
Пороговый уровень |
Процент скидки |
Good |
100,00 USD |
1% |
Excellent |
50,00 USD |
2% |
В таком случае, сервис должен позволять менять эти данные. Обновление может быть организовано так, чтобы обновляемыми были отдельные элементы или чтобы обновлялась вся таблица сразу. Вот пример поведенческого теста для индивидуального обновления.
Даны текущие данные.
Категория клиентов |
Пороговый уровень |
Процент скидки |
Good |
100,00 USD |
1% |
Excellent |
50,00 USD |
2% |
Когда элемент обновляется.
Категория клиентов |
Пороговый уровень |
Процент скидки |
Excellent |
50,00 USD |
3,5% |
Тогда обновленные данные.
Категория клиентов |
Пороговый уровень |
Процент скидки |
Good |
100,00 USD |
1% |
Excellent |
50,00 USD |
3,5% |
Можно также проверить, что обновленные данные используются для расчета скидки.
Категория клиентов |
Пороговый уровень |
Сумма скидки? |
Excellent |
100,00 USD |
3,50 USD |
У сервиса скидок может быть локальное хранилище для сохранения данных в этом примере, но он может зависеть и от отдельного сервиса хранения этих данных. Если так, то тесты из предыдущего раздела применяются и к отдельному сервису. Но каждая зависимость добавляет проблемы. Каким должно быть поведение службы, если ее зависимости недоступны? Для сервиса скидок это должно указывать на сбой или он должен просто возвращать значение по-умолчанию, тот же 0? Иногда можно использовать глобальные политики обработки ошибок, но часто решение зависит от контекста сервиса.
Формулирование тестов и автоматизация
После того, как поведение микросервиса согласовано, его можно сформулировать в виде автоматизированных тестов. Существует несколько систем тестирования микросервисов, таких как PACT или Karate. Кроме того, вы можете использовать BDD-фреймворки, такие как Cucumber или FIT.
Например Cucumber, использует библиотеки для запросов к сервисам. Тогда дополнительная информация об окружении может быть представлена ??как часть сценария.
Например, файл объектов Cucumber может включать.
Scenario: Compute discount for an order amount Given setup is: | URL | myrestservice.com | When discount computed with: | Method | GET | | Path | discount | | Version| 1 | Then results for each instance are: | Customer Category | Order Amount | Discount Amount? | | Good | 100.00 USD | 1.00 USD | | Excellent | 100.00 USD | 2.00 USD | |
Варианты шагов зависят от ваших соглашений тестирования.
Значения в первых двух столбцах могут быть перенесены в любое соглашение о вызовах, например, в параметры запроса. Результат в body должен соответствовать третьему столбцу. Если имена и значения запроса — это имена и значения столбцов, это уменьшает различия между тестом и реализацией.
Для повторного использования шаги могут быть написаны для произвольного сервиса, который делает вычисления или определяет результат выполнения бизнес-правила. В приведенном выше примере использование символа «?», как в «Discount Amount» выше, помогает анализатору различать входные и выходные данные.
Тесты также должны включать проверки на отказы, например.
Customer Category | Order Amount  | Discount Amount?  | Result |
Good  | 100.00 USD  |
1.00 USD  |
OK |
Not so Good  | 100.00 USD  | 2.00 USD  | Parameter value invalid |
Excellent  | 100.00 ZZZ  | 2.00 USD  | Parameter value invalid |
Заключение
Микросервисы зависят от других сервисов и систем, что требует четкой спецификации интерфейсов и их аккуратное тестирование. Этого можно добиться с помощью описания поведения и интерфейсов, заданных с помощью тестов. С помощью BDD, функциональность сервисов описывается исполняемыми тестами, которые фокусируются на семантике операций, а не на синтаксисе. Автоматизация таких тестов обычно требует настройки заглушек других сервисов, поведение которых описываются их отдельными BDD-тестами.
Интерфейсно-ориентированное проектирование — IOD, включает дополнительные обязательства сервиса: ограничение на использование ресурсов, пропускная способность и отчеты об ошибках. Вместе BDD и IOD помогают описать поведение сервиса, чтобы клиенты могли легко понять и положиться на него.
- BDD для микросервисов концентрируется на сотрудничестве триады — разработчика сервиса, разработчика клиента и тестировщика.
- Создавайте четко-описанные соглашения для интерфейсов микросервисов, используя IOD.
- Микросервисы обычно требуют тестовые заглушки для ускорения тестирования.
- Тесты должны быть независимые.
- Проверяйте в тестах негативные сценарии.
27-28 мая, в рамках фестиваля РИТ++ на конференции QualityConf Артём Малышев расскажет про важность ясного выражения доменной модели в коде и покажет как это делать на примерах.
Приходите общаться про разработку качественных продуктов и делиться своими идеями!