Разделы портала

Онлайн-тренинги

.
Введение в тестирование контрактов, часть 5: адаптация к изменениям
27.05.2022 00:00

Автор: Баз Дейкстра (Bas Dijkstra)
Оригинал статьи
Перевод: Ольга Алифанова

В предыдущей статье мы остановились на том, что у нас все в порядке с интеграционным тестированием: провайдер Address способен удовлетворить ожидания потребителей Customer и Order. Затем мы успешно справились с автоматизацией генерации контрактов и их публикации. Мы также автоматизировали их загрузку, проверку и публикацию результатов верификации на стороне провайдера.

Однако, как все мы знаем, системы постоянно меняются, и в них часто добавляются новые требования и функции. В этой статье мы рассмотрим ситуацию, когда на стороне потребителя внедряются два различных требования, а затем разберемся, как это повлияет на результаты верификации со стороны провайдера и предположим, как справляться с проблемами интеграции в ходе тестирования контрактов.

Добавление нового поля: поддержка абонентских ящиков

Потребители нашего онлайн-магазина сэндвичей просят возможности указать абонентский ящик в качестве адреса выставления счета. По очевидным причинам ящики не будут использоваться в качестве адреса доставки! Иными словами, адреса, которые ожидает наш потребитель Customer, теперь имеют поле "Абонентский ящик". Это целое число, принимающее значение 0, если номер ящика неизвестен.

Помните: нам не важно реальное значение – оно относится к бизнес-логике и должно покрываться тестами на стороне провайдера, а способность обрабатывать эти значения должна быть покрыта тестами на стороне потребителя. Тестирование контрактов в этом не задействовано – оно только убеждается, что поле действительно присутствует, и что значение в нем – целое число.

Обновленные адресные данные теперь будут выглядеть как-то так:

{
"id": "87256abc-f6b3-4e91-9f60-3ca3f54863d5",
"address_type": "billing",
"street": "Main Street",
"number": 123,
"poBox": 9876,
"city": "Nothingville",
"zip_code": 54321,
"state": "Tennessee",
"country": "United States"
}

а обновленные ожидания от тела ответа в Pact – вот так:

DslPart body = LambdaDsl.newJsonBody((o) -> o
.uuid("id", ID)
.stringType("addressType", ADDRESS_TYPE)
.stringType("street", STREET)
.integerType("number", NUMBER)
.integerType("poBox", POBOX)  // <-- это новая строка
.stringType("city", CITY)
.integerType("zipCode", ZIP_CODE)
.stringType("state", STATE)
.stringType("country", COUNTRY)
).build();

Естественно, мы также обновляем юнит-тест, проверяющий, что наш потребитель Customer может верно обработать обновленный ответ. Мы добавляем утверждение в существующий тест:

assertThat(address.getPoBox()).isEqualTo(POBOX);

Вот и все с точки зрения потребителя Customer. Повторный запуск этих тестов покажет, что все тесты успешно пройдены, и когда мы ожидаем генерации обновленного контракта, то видим новое добавленное ожидание для нового поля poBox:

"$.poBox": {
"combine": "AND",
"matchers": [
{
"match": "integer"
}
]
}

Когда мы публикуем новый контракт для верификации провайдером Address, то видим, что он не может соответствовать новому ожиданию:

[ERROR]   Run 3: ContractTest
Failures:
 
1) A request for address data has a matching body
 
1.1) body: $ Actual map is missing the following keys: poBox
 
{
"addressType": "billing",
"city": "Nothingville",
"country": "United States",
"id": "8aed8fad-d554-4af8-abf5-a65830b49a5f",
"number": 123,
"poBox": 9876,
"state": "Tennessee",
"street": "Main Street",
"zipCode": 54321
}

Как можно видеть, результат работы Pact явно сообщает, что реальный ответ, возвращенный провайдером, не включает новое поле poBox – имеется конфликт интеграции.

После обсуждения команда Address, отвечающая за разработку, тестирование и запуск провайдера Address, решила добавить поле poBox в ответ, направляемый потребителям Customer и Order. Тесты верификации контракта снова успешно выполняются:

[INFO] Results:
[INFO]
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  15.919 s
[INFO] Finished at: 2021-11-03T10:31:27+01:00
[INFO] ------------------------------------------------------------------------

Все вновь прекрасно в мире контрактного тестирования. Как минимум, пока…

Расширение рынка: поддержка канадских индексов

В связи с повышенным спросом на сэндвичи в Канаде наш магазин решает поддерживать канадские адреса доставки. Сейчас из-за связанных с налогообложением причин только адреса США принимаются в качестве адресов выставления счета.

Чтобы это реализовать, команда потребителя Order меняет свои ожидания от поля индекса – теперь это строка, а не целое число, потому что индексы в Канаде содержат и буквы, и цифры. Они решают не использовать регулярные выражения для проверки индекса, чтобы в будущем смочь поддерживать и другие индексы:

DslPart body = LambdaDsl.newJsonBody((o) -> o
.uuid("id", ID)
.stringType("addressType", ADDRESS_TYPE)
.stringType("street", STREET)
.integerType("number", NUMBER)
.stringType("city", CITY)
.stringType("zipCode", ZIP_CODE)  // <-- это новая строка
.stringType("state", STATE)
.stringType("country", COUNTRY)
).build();

Они соответственно обновляют свои юнит-тесты и запускают их, чтобы создать обновленный контракт.

Публикуя этот контракт для провайдера Address и верификации, мы видим, что он не соответствует новому ожиданию:

[ERROR]   Run 3: ContractTest
Failures:
 
1) A request for address data has a matching body
 
1.1) body: $.zipCode Expected 54321 (Integer) to be the same type as "54321" (String)

Если мы автоматически публикуем контракты и результаты верификации в Pactflow Pact Broker, как мы делали в предыдущей статье, мы увидим там ту же самую ошибку:


Однако, в отличие от предыдущего изменения, провайдер Address не может просто обновить свою реализацию, чтобы выполнить ожидания и Customer, и Order. Один из потребителей ожидает, что индекс будет типа String, другой ожидает Integer, но Address может вернуть только что-то одно, не оба сразу!

Это значит, что все три стороны должны обсудить наилучший способ решения этой проблемы. Это яркий пример проблем интеграции, стартующих на одной стороне, но затрагивающих другие команды, и выявленных при помощи контрактного тестирования.

В шестой, заключительной, статье мы внедрим еще одно действующее лицо в процесс контрактного тестирования и разберемся, как Pact и экосистема PactFlow упрощают внедрение тестирования контрактов при помощи двусторонних контрактов.

Код из статьи можно найти здесь.

Обсудить в форуме