From 5a2f02e3ef75076e68437f187745391e43fb9983 Mon Sep 17 00:00:00 2001 From: "s.tikhomirov" Date: Sun, 7 Aug 2022 23:49:44 +0300 Subject: [PATCH] Additional document --- README.md | 2 + .../source/quick_start/index.md | 2 +- .../source/spring/mock.mvc/index.md | 33 +- .../source/spring/mock.mvc/response.md | 2 +- .../source/spring/web.test.client/index.md | 209 +++++++- .../source/spring/web.test.client/response.md | 431 ++++++++++++++++ .../webtestclient_dependencies.rst | 41 ++ spring.web.testclient/README.md | 49 -- spring.web.testclient/doc/eng/README.MD | 0 spring.web.testclient/doc/rus/README.MD | 459 ------------------ 10 files changed, 702 insertions(+), 526 deletions(-) create mode 100644 site.documentation/source/spring/web.test.client/response.md create mode 100644 site.documentation/source/spring/web.test.client/webtestclient_dependencies.rst delete mode 100644 spring.web.testclient/README.md delete mode 100644 spring.web.testclient/doc/eng/README.MD delete mode 100644 spring.web.testclient/doc/rus/README.MD diff --git a/README.md b/README.md index dc5100ec18..3dc3b8e947 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,5 @@ First public BETA-versions are coming soon [Issues to be fixed/Requred features](https://github.com/Tinkoff/neptune/issues) [Change list](https://github.com/Tinkoff/neptune/releases) + +Site: [https://tinkoff.github.io/neptune/site/](https://tinkoff.github.io/neptune/site/) diff --git a/site.documentation/source/quick_start/index.md b/site.documentation/source/quick_start/index.md index e627b09bd7..e463426eb0 100644 --- a/site.documentation/source/quick_start/index.md +++ b/site.documentation/source/quick_start/index.md @@ -8,7 +8,7 @@ - Maven или gradle -- ⚠️ Необходим доступ к maven-репозиторию [https://nexus-new.tcsbank.ru/repository/mvn-bigops-qa](https://nexus-new.tcsbank.ru/repository/mvn-bigops-qa)_. +- ⚠️ _Необходим доступ к maven-репозиторию [https://nexus-new.tcsbank.ru/repository/mvn-bigops-qa](https://nexus-new.tcsbank.ru/repository/mvn-bigops-qa)_. Если доступа нет, то рекомендуется необходимые модули собрать локально. См [документ для контрибъюторов](start_to_contribute.md) - Указать в зависимостях необходимые модули. Описание каждого модуля см. соответствующей секции документации. diff --git a/site.documentation/source/spring/mock.mvc/index.md b/site.documentation/source/spring/mock.mvc/index.md index e68d95317f..1f1de99564 100644 --- a/site.documentation/source/spring/mock.mvc/index.md +++ b/site.documentation/source/spring/mock.mvc/index.md @@ -13,7 +13,7 @@ [API](https://tinkoff.github.io/neptune/spring.mock.mvc/index.html) -## Почему _Neptune_ ? +## Сравнение _Neptune + MockMVC_ с другими вариантами Ниже небольшое сравнение того как выглядит один и тот же тест: @@ -24,6 +24,7 @@ ### Тест с использованием Mock MVC, без реализации шагов ```java + @SpringBootTest @AutoConfigureMockMvc public class SomeTest { @@ -65,9 +66,9 @@ public class SomeTest { var someCalculatedValue = //вычисление чего-то с использованием //responseDto - assertThat("Some calculated value", - someCalculatedValue.getSomethingElse(), - is(someExpeсtedValue2)); + assertThat("Some calculated value", + someCalculatedValue.getSomethingElse(), + is(someExpeсtedValue2)); //и т.д. } @@ -80,6 +81,7 @@ public class SomeTest { какие действия выполняются и каков их результат. Тогда ```java + @SpringBootTest @AutoConfigureMockMvc public class SomeTest { @@ -152,7 +154,7 @@ public class SomeTest { var someCalculatedValue = //вычисление чего-то с использованием //responseDto - assertSomeCalculatedValue(someCalculatedValue, someExpectedValue2); + assertSomeCalculatedValue(someCalculatedValue, someExpectedValue2); //и т.д. } } @@ -161,10 +163,11 @@ public class SomeTest { ### Тест с использованием Mock MVC и Neptune ```java + @SpringBootTest @AutoConfigureMockMvc public class SomeTest { - + @Autowired private ObjectMapper objectMapper; @@ -182,18 +185,18 @@ public class SomeTest { // будут проверены все ожидания .expectStatus(200) //проваленные будут выделены в отчете .expectJsonPathValue("$.successOrder", 5) - //предусмотрен механизм десериализации + //предусмотрен механизм десериализации .thenGetBody(SomeResponseDto.class) ); - check("Response body", - responseDto, - match("Some field value", - SomeResponseDto::getSomething, - is(someExpectedValue)), - match("Some calculated value", - dto -> {/*вычисление чего-то с использованием*/}, - is(someExpeсtedValue2))); + check("Response body", + responseDto, + match("Some field value", + SomeResponseDto::getSomething, + is(someExpectedValue)), + match("Some calculated value", + dto -> {/*вычисление чего-то с использованием*/}, + is(someExpeсtedValue2))); } } ``` diff --git a/site.documentation/source/spring/mock.mvc/response.md b/site.documentation/source/spring/mock.mvc/response.md index 2eeef97bdf..f864478aed 100644 --- a/site.documentation/source/spring/mock.mvc/response.md +++ b/site.documentation/source/spring/mock.mvc/response.md @@ -1,4 +1,4 @@ -# Request / Response +# MockMVC. Request / Response ```java import org.springframework.boot.test.context.SpringBootTest; diff --git a/site.documentation/source/spring/web.test.client/index.md b/site.documentation/source/spring/web.test.client/index.md index 23ee80bde4..3c59a7a21a 100644 --- a/site.documentation/source/spring/web.test.client/index.md +++ b/site.documentation/source/spring/web.test.client/index.md @@ -1 +1,208 @@ -# Web Test Client \ No newline at end of file +# Web Test Client + +Данный модуль: + +- является дополнением к + стандартному [Spring WebTestClient](https://spring.getdocs.org/en-US/spring-framework-docs/docs/testing/integration-testing/webtestclient.html) +- данный модуль предоставляет фасад , который фиксирует работу _Spring WebTestClient_ в виде шагов +- данный модуль исправляет потенциальные неудобства флоу _Spring WebTestClient_ + +```{eval-rst} +.. include:: webtestclient_dependencies.rst +``` + +[API](https://tinkoff.github.io/neptune/spring.web.testclient/index.html) + +## ## Сравнение _Neptune + WebTestClient_ с другими вариантами + +Ниже небольшое сравнение того как выглядит один и тот же тест: + +- с использованием WebTestClient, без реализации шагов +- с использованием WebTestClient и с реализацией шагов +- с использованием WebTestClient и Neptune + +### Тест с использованием WebTestClient, без реализации шагов + +```java + +@SpringBootTest +@AutoConfigureWebTestClient +public class SomeTest { + + @Autowired + private WebTestClient client; + + @Test + public void semeAPITest() { + SomeResponseDto responseDto = client.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class) + .exchange() + //Если текущее ожидание не выполнилось, + //на нем тест остановится и последующие ожидания не будут + //проверены. Хотелось бы видеть более полную картину несоответствий + //до того, как начать багофикс + .expectStatus().isOk() + //хотелось бы частые ожидания иметь + //в более доступном и коротком виде + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody(SomeResponseDto.class) + .returnResult() + .getResponseBody(); + + //дельнейшие вычисления + //проверка поля ответа + assertThat("Some field value", + responseDto.getSomething(), + is(someExpectedValue)); + + var someCalculatedValue = //вычисление чего-то с использованием + //responseDto + + assertThat("Some calculated value", + someCalculatedValue.getSomethingElse(), + is(someExpeсtedValue2)); + + //и т.д. + } +} +``` + +### Тест с использованием WebTestClient и с реализацией шагов + +Предположим, что результат интеграционного теста должен быть оформлен в отчет, описывающий по шагам, +какие действия выполняются и каков их результат. Тогда + +```java + +@SpringBootTest +@AutoConfigureWebTestClient +public class SomeTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private WebTestClient client; + + @Step("Подготовить тело запроса") + private SomeDTO prepareRequestBody() { + var toReturn = new SomeDTO(); + addAttachment("Request body", "application/json", + objectMapper.writeValueAsString(toReturn)); + return toReturn; + } + + @Step("Получаем ответ на запрос POST /something") + private ResponseSpec getResponsePostSpec(SomeDTO body) { + var postExchangeSpec = client.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class) + .exchange(); + + //делаем аттачи, фиксируем параметры и т.д. + return postExchangeSpec; + } + + @Step("Выполняем проверку ответа и возвращаем прочитанное тело") + private SomeResponseDto getSomeResponseDto( + ResponseSpec responseSpec) { + + SomeResponseDto bodyContent = responseSpec.expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody(SomeResponseDto.class) + .returnResult() + .getResponseBody(); + + addAttachment("Response body", "application/json", + objectMapper.writeValueAsString(bodyContent)); + return bodyContent; + } + + @Step("Проверить тело ответа") + private void assertSomeDTO(SomeResponseDto toCheck, Object expected) { + assertThat("Some field value", + responseDto.getSomething(), + is(expected)); + } + + @Step("Проверить полученное у тела значение") + private void assertSomeCalculatedValue(SomeCalculatedValue toCheck, Object expected) { + assertThat("Some calculated value", + someCalculatedValue.getSomethingElse(), + is(expected)); + } + + + @Test + public void semeAPITest() { + //Тест стал короче, НО!!!! + //У нас появился толстый слой шагов, который надо организовывать + //и поддерживать. + //Может начаться дублирование кода, если аналогичные действия + //встречаются в других тестах. + //Организация библиотеки шагов сделает тест непрозрачным, + //в перспективе затруднит модификацию/рефактринг тестов + + var body = prepareRequestBody(); + var response = getResponsePostSpec(body); + var dto = getSomeResponseDto(response); + assertSomeDTO(dto, someExpectedValue); + + var someCalculatedValue = //вычисление чего-то с использованием + //responseDto + assertSomeCalculatedValue(someCalculatedValue, someExpectedValue2); + //и т.д. + } +} +``` + +### Тест с использованием WebTestClient и Neptune + +```java + +@SpringBootTest +@AutoConfigureWebTestClient +public class SomeTest { + + //Поле ниже можно не объявлять + //@Autowired + //private WebTestClient client; + + @Test //все описанное в тесте сформирует шаги разной вложенности + //и автоматически сформирует аттачи + public void semeAPITest() { + SomeResponseDto responseDto = webTestClient( + send(webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class), + Dto.class) + // будут проверены все ожидания + .expectStatus(200) //проваленные будут выделены в отчете + .expectContentType(APPLICATION_JSON) + .thenGetBody() + ); + + check("Response body", + responseDto, + match("Some field value", + SomeResponseDto::getSomething, + is(someExpectedValue)), + match("Some calculated value", + dto -> {/*вычисление чего-то с использованием*/}, + is(someExpeсtedValue2))); + } +} +``` + +```{toctree} +:hidden: + +response.md +``` \ No newline at end of file diff --git a/site.documentation/source/spring/web.test.client/response.md b/site.documentation/source/spring/web.test.client/response.md new file mode 100644 index 0000000000..0f19715419 --- /dev/null +++ b/site.documentation/source/spring/web.test.client/response.md @@ -0,0 +1,431 @@ +# WebTestClient. Request / Response + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring.web.testclient + .WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test(description = "Простое выполнение запроса и получение ответа") + public void myTest() { + webTestClient( + //описывается запрос: + // метод (GET, POST, PUT или Delete) + send(webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) //и + //параметры + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class), + //Опциональный параметр, который можно не указывать. + //От его наличия зависит, как будут сформированы + //ожидания к контенту ответа, как к dto, или + //как к тексту/массиву байтов + Dto.class) + ); + } +} +``` + +Ниже пример с явным использованием объекта `org.springframework.test.web.reactive.server.WebTestClient` + +```java +import org.springframework.test.web.reactive.server.WebTestClient; + +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring.web.testclient + .WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Autowired //Или любой другой вариант + private WebTestClient client; //инициализации поля или переменной + + @Test(description = "Простое выполнение запроса и получение ответа") + public void myTest() { + webTestClient( + send(client, //явная передача объекта WebTestClient + webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) //и + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class), + Dto.class) + ); + } +} +``` + +## Описание ожиданий + +Методы, с помощью которых можно описать ожидаемый ответ, можно найти: + +- в документации [общего описания отправки запроса. Названия методов начинаются на _ + expect_](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/SendRequestAction.html) + . +- в документации описания отправки + запросов, [тела ответов на которые читаются как текст/массив байтов. Названия методов начинаются на _ + expect_](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/SendRequestActionRaw.html) + . +- в документации описания отправки запросов, [тела ответов на которые читаются как DTO. Названия методов начинаются на _ + expect_](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/SendRequestActionMapped.html) + . + +Ниже простой пример + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring.web.testclient + .WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test(description = "Простое выполнение запроса и получение ответа") + public void myTest() { + webTestClient( + send(webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) //и + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class)) + // Часто используемые ожидания имеют + .expectStatus(200) //упрощенную запись + .expectContentType(APPLICATION_JSON) + ///////////////////////////////////////// + // + //Так же, при необходимости, есть возможности описывать + //ожидания в функциональном стиле + .expectStatus(statusAssertions -> { + //некое вычисление или алгоритм + return statusAssertions.isOk(); + }) + .expectContent(headerAssertions -> { + //некое вычисление или алгоритм + return headerAssertions.contentType(APPLICATION_JSON); + }) + ); + } +} +``` + +Пример описания ожидания к телу ответа, которое прочитано как текст/массив байтов + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring.web.testclient + .WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test(description = "Простое выполнение запроса и получение ответа") + public void myTest() { + webTestClient( + send(webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) //и + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class)) + //можно описывать ожидания к json/xml тексту + //или к массиву байтов + .expectBodyJsonPathEquals("some.path", 1) + ); + } +} +``` + +Пример описания ожидания к телу ответа, которое прочитано как DTO + +```java +import static org.hamcrest.Matchers.*; + +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring.web.testclient + .WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test(description = "Простое выполнение запроса и получение ответа") + public void myTest() { + webTestClient( + send(webClient -> webClient.post() + .uri("/something") + .contentType(MediaType.APPLICATION_JSON) //и + .accept(MediaType.APPLICATION_JSON) + .body(Mono.just(new SomeDTO()), SomeDTO.class), Dto.class) + //ожидание для всего тела + .expectBody(equalTo(new Dto())) + //описание того, ЧТО проверяется + .expectBody("Some field value", + //описываем, как получить проверяемое значение + Dto::someField, + equalTo(someValue)) + ); + } +} +``` + +## Тело ответа + +Важно: + +- [Шаги, возвращающие объекты](./../../core/steps/steps/get_step_supplier/index.md) + +### Прочитанное как текст / массив байтов + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { //когда не указано, в какие объекты должна +//происходить десериализация тела ответа + + @Test + public void myTest() { + //по умолчанию тело ответа представляется как массив байтов + byte[] body = webTestClient(send(/*параметры запроса*/) + //ожидания + .thenGetBody() + ); + + //так же можно вернуть тело ответа в виде строки + String body2 = webTestClient(send(/*параметры запроса*/) + //ожидания + .thenGetBodyAsString() + ); + } +} +``` + +### Прочитанное как DTO + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test + public void myTest() { + Dto body = webTestClient(send(/*параметры запроса*/, + //так же можно использовать + //org.springframework.core.ParameterizedTypeReference + Dto.class) + //ожидания + .thenGetBody()); + + Object value = webTestClient(send(/*параметры запроса*/, + Dto.class) + //ожидания + // + // описание объекта, который следует получить + .thenGetValue("Value of the field 'getSomeValue'", + //описание получаемого результата в виде функции + Dto::getSomeValue) + //можно указать один или несколько критериев, + // которым должен соответствовать получаемый объект + .criteria("Описание критерия, " + + "которому должен соответствовать " + + "получаемый объект", dto -> { + /*предикат, как работает критерий*/ + }) + //можно указать, что должно быть выброшено исключение, + // если получаемый объект пустой / не соответствует + // перечисленным критериям + .throwOnNoResult() + ); + } +} +``` + +### List + +```java +import java.util.List; + +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test + public void myTest() { + + List value = webTestClient(send(/*параметры запроса*/, + //так же можно использовать + //org.springframework.core.ParameterizedTypeReference + Dto.class) + //ожидания + // + //описание листа, который следует получить + .thenGetList("Value of the field 'listValue'", + //описание получения списка в виде функции + Dto::listValue) + //можно указать один или несколько критериев, + // которым должен соответствовать каждый элемент + // результирующего листа + .criteria("Описание критерия, " + + "которому должен соответствовать " + + "каждый элемент, " + + "который попадет в результирующий лист", o -> { + /*предикат, как работает критерий*/ + }) + //можно указать, что должно быть выброшено исключение, + // если результирующий список пустой + .throwOnNoResult()); + } +} +``` + +### Массив + +```java +import java.util.List; + +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test + public void myTest() { + + List value = webTestClient(send(/*параметры запроса*/, + //так же можно использовать + //org.springframework.core.ParameterizedTypeReference + Dto.class) + //ожидания + // + //описание листа, который следует получить + .thenGetArray("Value of the field 'arrayValue'", + //описание получения списка в виде функции + Dto::arrayValue) + //можно указать один или несколько критериев, + // которым должен соответствовать каждый элемент + // результирующего массива + .criteria("Описание критерия, " + + "которому должен соответствовать " + + "каждый элемент, " + + "который попадет в результирующий массив", o -> { + /*предикат, как работает критерий*/ + }) + //можно указать, что должно быть выброшено исключение, + // если результирующий массив пустой + .throwOnNoResult()); + } +} +``` + +### Элемент Iterable + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test + public void myTest() { + + Object value = webTestClient(send(/*параметры запроса*/, + //так же можно использовать + //org.springframework.core.ParameterizedTypeReference + Dto.class) + //ожидания + // + //описание объекта, который следует получить + .thenGetValueFromIterable("A value from " + + "the field 'listValue'", + //описание получения списка в виде функции + Dto::listValue) + //можно указать один или несколько критериев, + // которым должен соответствовать результирующий + // элемент листа + .criteria("Описание критерия, " + + "которому должен соответствовать " + + "результирующий элемент листа", o -> { + /*предикат, как работает критерий*/ + }) + //можно указать, что должно быть выброшено исключение, + // если результирующий элемент пустой + .throwOnNoResult()); + } +} +``` + +### Элемент массива + +```java +import static ru.tinkoff.qa.neptune.spring + .web.testclient.SendRequestAction.send; +import static ru.tinkoff.qa.neptune.spring + .web.testclient.WebTestClientContext.webTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +public class MyTest { + + @Test + public void myTest() { + + Object value = webTestClient(send(/*параметры запроса*/, + //так же можно использовать + //org.springframework.core.ParameterizedTypeReference + Dto.class) + //ожидания + // + //описание объекта, который следует получить + .thenGetValueFromArray("A value from " + + "the field 'arrayValue'", + //описание получения списка в виде функции + Dto::arrayValue) + //можно указать один или несколько критериев, + // которым должен соответствовать результирующий + // элемент массива + .criteria("Описание критерия, " + + "которому должен соответствовать " + + "результирующий элемент массива", o -> { + /*предикат, как работает критерий*/ + }) + //можно указать, что должно быть выброшено исключение, + // если результирующий элемент пустой + .throwOnNoResult()); + } +} +``` + diff --git a/site.documentation/source/spring/web.test.client/webtestclient_dependencies.rst b/site.documentation/source/spring/web.test.client/webtestclient_dependencies.rst new file mode 100644 index 0000000000..71071d8c60 --- /dev/null +++ b/site.documentation/source/spring/web.test.client/webtestclient_dependencies.rst @@ -0,0 +1,41 @@ +.. code-block:: xml + :caption: maven/dependencies + + + + + org.springframework.boot + spring-boot-starter-webflux + + [2.6.1,) + + + + + org.springframework.boot + spring-boot-starter-test + + [2.6.1,) + test + + + + ru.tinkoff.qa.neptune + spring.web.testclient + ${LATEST_RELEASE_OR_BETA_VERSION} + test + + + +.. code-block:: groovy + :caption: Добавить в build.gradle + + dependencies { + dependencies { + //необходимо иметь в classpath + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '[2.6.1,)' //диапазон поддерживаемых версий + //необходимо иметь в test classpath + testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '[2.6.1,)' //диапазон поддерживаемых версий + testImplementation group: 'ru.tinkoff.qa.neptune', name: 'spring.web.testclient', version: LATEST_RELEASE_OR_BETA_VERSION + } + } \ No newline at end of file diff --git a/spring.web.testclient/README.md b/spring.web.testclient/README.md deleted file mode 100644 index ad4f80b5c4..0000000000 --- a/spring.web.testclient/README.md +++ /dev/null @@ -1,49 +0,0 @@ -Integration of Neptune with [Spring WebTestClient](https://spring.getdocs.org/en-US/spring-framework-docs/docs/testing/integration-testing/webtestclient.html) - -## Maven - -```xml - - - - org.springframework.boot - spring-boot-starter-webflux - - [2.6.1,) - - - - - org.springframework.boot - spring-boot-starter-test - - [2.6.1,) - test - - - - ru.tinkoff.qa.neptune - spring.web.testclient - ${LATEST_RELEASE_OR_BETA_VERSION} - test - - -``` - -## Gradle - -```groovy - dependencies { - //it is necessary to have this in classpath - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '[2.6.1,)' //range of supported versions - //it is necessary to have this in test classpath - testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '[2.6.1,)' //range of supported versions - testImplementation group: 'ru.tinkoff.qa.neptune', name: 'spring.web.testclient', version: LATEST_RELEASE_OR_BETA_VERSION - } -``` - -[Краткая документация на русском](./doc/rus/README.MD) - -[Brief documentation in English](./doc/eng/README.MD) - -[API overview](https://tinkoff.github.io/neptune/spring.web.testclient/index.html) \ No newline at end of file diff --git a/spring.web.testclient/doc/eng/README.MD b/spring.web.testclient/doc/eng/README.MD deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/spring.web.testclient/doc/rus/README.MD b/spring.web.testclient/doc/rus/README.MD deleted file mode 100644 index e201c011bc..0000000000 --- a/spring.web.testclient/doc/rus/README.MD +++ /dev/null @@ -1,459 +0,0 @@ -# Работа с WebTestClient - -- О контекстах можно прочитать [здесь](./../../../core.api/doc/rus/STEPS.MD#Контекст). - Объект [контекста для работы со Spring WebTestClient](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/WebTestClientContext.html) - в примерах ниже вызывается статическим методом `webTestClient` - -- О принципах работы шагов, которые возвращают результат, можно - прочитать [здесь](./../../../core.api/doc/rus/STEPS.MD#Шаги-которые-возвращают-результат). - -- О принципах работы шагов, которые выполняют действие, можно - прочитать [здесь](./../../../core.api/doc/rus/STEPS.MD#Шаги-которые-выполняют-действие). - -## Оглавление - -- [Отправка запроса и получение ответа](#Отправка-запроса-и-получение-ответа) - - [Получение ответа без десериализации его тела](#Получение-ответа-без-десериализации-его-тела) - - [Получение ответа с десериализацией его тела](#Получение-ответа-с-десериализацией-его-тела) -- [Получение данных тела ответа](#Получение-данных-тела-ответа) - - [Получение данных тела ответа как Java-объект](#Получение-данных-тела-ответа-как-Java-объект) - - [Получение данных тела ответа как List](#Получение-данных-тела-ответа-как-List) - - [Получение данных тела ответа в виде массива](#Получение-данных-тела-ответа-в-виде-массива) - - [Получение данных тела ответа как Java-объект из Iterable](#Получение-данных-тела-ответа-как-Java-объект-из-Iterable) - - [Получение данных тела ответа как Java-объект из массива](#Получение-данных-тела-ответа-как-Java-объект-из-массива) - -## Отправка запроса и получение ответа - -### Получение ответа без десериализации его тела - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - //отправка запроса с получением ответа и его проверкой - webTestClient(send(webClient -> webClient.post() //запрос - .uri("/some/path")) //и его параметры - //Далее можно указать параметры ожидаемого ответа. - //Проверка пройдет по всем - // указанным ожиданиям. Какие-то из них не будут удовлетворены, - //будет выброшено AssertError с описанием всех несоответствий - //Так же каждое отдельное несоответствие можно будет найти в отчете, - //логе или консоли - .expectStatus(StatusAssertions::isOk) //можно указать ожидание статуса ответа - .expectHeader(headerAssertions -> headerAssertions.contentType(TEXT_PLAIN)) //можно указать ожидания для заголовков ответа - .expectCookie(cookieAssertions -> cookieAssertions.exists("someCookie")) //можно указать ожидания для cookie ответа - //те же ожидания, что и выше, - .expectStatus(200) //записанные в упрощенной форме - .expectContentType(TEXT_PLAIN) - .expectCookie("someCookie") - //можно описать полностью или частично ожидаемое тело ответа - .expectBodyJson("{\"someField\"=\"someValue\"}") // в json-формате - .expectBodyXml("someValue") // или xml-формате - .expectBodyJsonPath("$.someField", jsonPah -> jsonPah.isEqualTo("someValue")) //либо ожидания в виде json-path - .expectBodyXpath("./someField", xpath -> xpath.isEqualTo("someValue")) //или в виде xPath, - .expectEmptyBody()); //либо указать, что ожидается пустое тело ответа - } -} -``` - -Методы, с помощью которых можно описать ожидаемый ответ, можно -[посмотреть в документации. Названия методов начинаются на _expect_](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/SendRequestActionRaw.html) - -Так же можно передавать значения `@Autowired`- полей или переменных пипа `WebTestClient` - -```java -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Autowired //Или любой другой вариант - private WebTestClient client; //инициализации поля или переменной - - @Test - public void myTest() { - webTestClient(send( - client, //явная передача инстанса - webClient -> webClient.post().uri("/some/path")) - .expectStatus(200) - .expectContentType(TEXT_PLAIN)); - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение ответа с десериализацией его тела - -```java -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import java.util.List; - -import static org.hamcrest.Matchers.*; -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - //отправка запроса с получением ответа и его проверкой - webTestClient(send(webClient -> webClient.post() //запрос - .uri("/some/path"), //и его параметры - Dto.class) //и в объект какого класса должно быть десериализовано тело ответа - //Далее можно указать параметры ожидаемого ответа. - //Проверка пройдет по всем - // указанным ожиданиям. Какие-то из них не будут удовлетворены, - //будет выброшено AssertError с описанием всех несоответствий - //Так же каждое отдельное несоответствие можно будет найти в отчете, - //логе или консоли - .expectStatus(StatusAssertions::isOk) //можно указать ожидание статуса ответа - .expectHeader(headerAssertions -> headerAssertions.contentType(TEXT_PLAIN)) //можно указать ожидания для заголовков ответа - .expectCookie(cookieAssertions -> cookieAssertions.exists("someCookie")) //можно указать ожидания для cookie ответа - //те же ожидания, что и выше, - .expectStatus(200) //записанные в упрощенной форме - .expectContentType(TEXT_PLAIN) - .expectCookie("someCookie") - //можно указать одно или несколько ожиданий для тела ответа с использованием матчеров, - .expectBody(equalTo(someDtoObject)) //которые описывают критерии для десериализованного тела целиком - //либо для полей десериализованного тела, или вычисляемых с его помощью значений - .expectBody("Some field value", //описание поля или проверяемого значения - dto -> dto.getSomeField(), //как получить проверяемое значение - equalTo(someObject)) //матчер для проверяемого значения - ); //либо указать, что ожидается пустое тело ответа - } - - @Test - public void myTest2() { - //актуально все, что в примере выше - webTestClient(send(webClient -> webClient.post() - .uri("/some/path2"), - new ParameterizedTypeRefere>() { //указывается ссылка на тип, - })); //в объект которого должно быть десериализовано тело ответа - } -} -``` - -Методы, с помощью которых можно описать ожидаемый ответ, можно -[посмотреть в документации. Названия методов начинаются на _expect_](https://tinkoff.github.io/neptune/spring.web.testclient/ru/tinkoff/qa/neptune/spring/web/testclient/SendRequestActionMapped.html) - -Так же можно передавать значения `@Autowired`- полей или переменных пипа `WebTestClient` - -```java -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Autowired //Или любой другой вариант - private WebTestClient client; //инициализации поля или переменной - - @Test - public void myTest() { - webTestClient(send( - client, //явная передача инстанса - webClient -> webClient.post().uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN)); - } -} -``` - -[к оглавлению документа](#Оглавление) - -## Получение данных тела ответа - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { //когда не указано, в какие объекты должна -//происходить десериализация тела ответа - - @Test - public void myTest() { - //по умолчанию тело ответа представляется как массив байтов - byte[] body = webTestClient(send(webClient -> webClient.post() - .uri("/some/path")) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetBody()); - } - - @Test - public void myTest2() { - //так же можно вернуть тело ответа в виде строки - String body = webTestClient(send(webClient -> webClient.post() - .uri("/some/path")) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetBodyAsString()); - } -} -``` - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest {//случай с десериализацией тела ответа - - @Test - public void myTest() { - - Dto body = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetBody()); - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение данных тела ответа как Java-объект - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - - Object value = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetValue("Value of the field 'stringValue'", //описание объекта, который следует получить - Dto::getSomeValue) //описание получаемого результата в виде функции - .criteria("Описание критерия, которому должен соответствовать получаемый объект", - o -> /*предикат, как работает критерий*/) //можно указать один или несколько критериев, - // которым должен соответствовать получаемый объект - .throwOnNoResult()); //можно указать, что должно быть выброшено исключение, - // если получаемый объект не соответствует перечисленным критериям, - // иначе - просто вернется null - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение данных тела ответа как List - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import java.util.List; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - - List value = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetList("Value of the field 'listValue'", //описание списка, который следует получить - Dto::listValue) //описание получения списка в виде функции - .criteria("Описание критерия, которому должен соответствовать " + - "каждый элемент, который попадет в результирующий список", - o -> /*предикат, как работает критерий*/) //можно указать один или несколько критериев, - // которым должен соответствовать каждый элемент результирующего списка - .throwOnNoResult()); //можно указать, что должно быть выброшено исключение, - // если результирующий список пустой (не было ни одного элемента, - //который бы соответствовал перечисленным критериям, или исходный список пуст) - // иначе - просто вернется null или пустой список - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение данных тела ответа в виде массива - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - - Object[] value = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetArray("Value of the field 'arrayValue'", //описание массива, который следует получить - Dto::arrayValue) //описание получения массива в виде функции - .criteria("Описание критерия, которому должен соответствовать " + - "каждый элемент, который попадет в результирующий массив", - o -> /*предикат, как работает критерий*/) //можно указать один или несколько критериев, - // которым должен соответствовать - //каждый элемент результирующего массива - .throwOnNoResult()); //можно указать, что должно быть выброшено исключение, - // если результирующий массив пустой (не было ни одного элемента, - //который бы соответствовал перечисленным критериям, или исходный массив пуст) - // иначе - просто вернется null или пустой массив - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение данных тела ответа как Java-объект из Iterable - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - - Object value = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetValueFromIterable("A value from the field 'listValue'", //описание объекта, который следует получить - Dto::listValue) //описание получения списка в виде функции - .criteria("Описание критерия, которому должен соответствовать " + - "результирующий элемент из списка", - o -> /*предикат, как работает критерий*/) //можно указать один или несколько критериев, - // которому должен соответствовать результирующий элемент из списка - .throwOnNoResult()); //можно указать, что должно быть выброшено исключение, - // если не был получен результирующий элемент (не было ни одного элемента, - //который бы соответствовал перечисленным критериям, или исходный список пуст) - // иначе - просто вернется null - } -} -``` - -[к оглавлению документа](#Оглавление) - -### Получение данных тела ответа как Java-объект из массива - -```java -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static ru.tinkoff.qa.neptune.spring.web.testclient.SendRequestAction.send; -import static ru.tinkoff.qa.neptune.spring.web.testclient.WebTestClientContext.webTestClient; - -@SpringBootTest -@AutoConfigureWebTestClient -public class MyTest { - - @Test - public void myTest() { - - Object value = webTestClient(send(webClient -> webClient.post() - .uri("/some/path"), - Dto.class) - .expectStatus(200) - .expectContentType(TEXT_PLAIN) - .thenGetValueFromArray("A value from the field 'arrayValue'", //описание объекта, который следует получить - Dto::arrayValue) //описание получения массива в виде функции - .criteria("Описание критерия, которому должен соответствовать " + - "результирующий элемент из массива", - o -> /*предикат, как работает критерий*/) //можно указать один или несколько критериев, - // которому должен соответствовать результирующий элемент из массива - .throwOnNoResult()); //можно указать, что должно быть выброшено исключение, - // если не был получен результирующий элемент (не было ни одного элемента, - //который бы соответствовал перечисленным критериям, или исходный массив пуст) - // иначе - просто вернется null - } -} -``` - -[к оглавлению документа](#Оглавление) \ No newline at end of file