Переизобретаем БДД тесты
TODO
Повышаем наглядность тестов.
Тест должен не только тестировать но и говорить мне какие запросы куда идут
Contract testing approach to reduce complexity for integration testing with services
Contract testing is a technique for testing an integration point by checking each application in isolation to ensure the messages it sends or receives conform to a shared understanding that is documented in a “contract”. - https://docs.pact.io/
The problem that initiative aims to address revolves around the challenges and intricacies involved in integration testing, especially when dealing with microservices or external APIs. Integration testing, which verifies that different parts of a system work together as intended, can become highly complex and cumbersome when multiple services are involved. The contract testing approach proposes a solution to these challenges by focusing on verifying the interactions between services at the API level based on predefined contracts.
Required to dive into contract testing and say what we can achieve.
Details: Pact, Spring contract
import org.springframework.cloud.contract.spec.Contract; https://docs.pact.io/books https://habr.com/ru/articles/451132/ https://habr.com/ru/companies/testit-tms/articles/570544/ https://github.com/spring-cloud/spring-cloud-contract/blob/main/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractProjectUpdater.java https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/main/producer_java/src/test/java/contracts/beer/intoxication/1_sober.java https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs/blob/sc-contract/contracts/1_shouldAddABook.yml https://softwaremill.com/contract-testing-with-pact/ https://medium.com/@ximna.inc/way-to-microservices-contract-testing-a-spring-pact-implementation-1140aff95d39
Ответ не определяется набором входящих параметров, потому что провайдер редко является чистой детерменированной функций (за исключением валидации). Из-за этого нужно вводить понятие состояние и именно им с учетом этого идентифицировать ответ от провайдера. А если мы вынуждены это делать, то роль входящих параметров это фикция - иллюзия отношения между запросом и ответом
The focus on the messages (request/response) not the implementation
Если заглянуть под капот к кукумбер, то там можно будет увидеть весь ад.
Arrange-Act-Assert снова, визуально выделяем блоки, интуитивно пониманием что все что сверху - arrange
Кукумбер идея классная, но язык бедный. Дает тоже самое, что drools - заставляет отделить интеграцию от бизнес логики и сосредоточиться на последней.
Цель - написать тест таким образом, чтобы снизить шум от интеграционного кода и выставить вперед бизнес логику
Предложение:
- spock
- request captor
- record captor
- rest expectation
- json assert
- идентификаторы для сценариев
Почему Spock
Почему request captor
Смотрите статью
Почему request captor
Смотрите статью
rest expectation
Обертка над моками для реста.
В статье https://habr.com/ru/articles/781812/ указан способ описание тестов, в итоге приходим к такому
0 def "Forecast for provided city London is 42"() {
1 setup:
2 def requestCaptor = new RequestCaptor()
3 mockServer.expect(manyTimes(),requestTo("https://weather.com/forecast"))
4 .andExpect(method(HttpMethod.POST))
5 .andExpect(requestCaptor)
6 .andRespond(withSuccess('{"result":"42"}',APPLICATION_JSON));
7 when:
8 def forecast = weatherService.getForecast("London")
9 then:
. forecast == "42"
. requestCaptor.times == 1
. requestCaptor.body.city == "London"
. requestCaptor.headers.get("Content-Type") == ["application/json"]
}
Строки с 2 по 6 можно заменить на 1: TODO лучше заменить на telegram
0 def "Forecast for provided city London is 42"() {
1 setup:
2 def requestCaptor = restExpectation.weather.forecast(withSuccess('{"result":"42"}'))
7 when:
8 def forecast = weatherService.getForecast("London")
9 then:
. forecast == "42"
. requestCaptor.times == 1
. requestCaptor.body.city == "London"
. requestCaptor.headers.get("Content-Type") == ["application/json"]
}
при этом мы сохраняем структуру запроса и ответа и можем быстро их понять
Почему json assert
Позволяет снизить визуальный шум за счет сокращения количества кода, нужного для осуществления сравнения.
Пример, когда не очевидно преимущество
Пример, когда очевидно преимущество TODO заменить на какой-то реальный пример
Одно из преимуществ - визуально структура явно видна, что нельзя сказать о варианте А, что выше.
Чего нет в jsonassrt - нельзя верифицировать порядок следования атрибутов. Улучшения для jsonassrt, позволяет добавить обработчики и проверять, например, наличие полей.
Спок уже бдд, добавим сахара
Вытаскиваем ну ружу то что хотим проверить, поведение и
Не будем ничего изобретать и воспользуемся тем что есть - груви, спок, джсон вссерт
Спрятать подальше особенности языка и фреймворка и принести поближе протокол
Кукумбер похож на фронтенд бекенд, где первый красивый и второй страшный, тянет все на себе. Картинка с монстром в воде. Показать слева кукумбер справа то что его поддерживает
-
рест экспектецшн
-
Json assert
-
Event bucket
Использование mvc для приемстаенности и сохранени знакомых якорей, чтобы не было шока
Требователен к тестам Наглядные тесты Пример: jsonassrt, event backet
Чтобы ответить на этот вопрос пришлось написать ряд статей в которых отразить подходы позволяющие писать тесты просто
Сделайте тесты частью свой работы
dsl для тестов
Интеграция и бизнес-логика
Если тестируем первое, то оно должно быть видно, если второе то второе
Не смешиваем интеграционные тесты и тесты бизнес логики
На примере бота для телеги