Rest assured что это

REST-assured: полезные советы

В данной статье я собрал полезные советы по использованию REST-assured, одной из самых распространенных Java-библиотек для автоматизации тестирования REST-API.

Все примеры жизненные, они собраны из моей практики проведения code-review в более чем 50 проектах с автотестами.

Выносите end-point’ы в отдельное место

Казалось бы, что это очевидно. Но нет, довольно часто приходится видеть код с захардкоженными end-point’ми в запросе.

Лучше всего выносить end-point’ы в статические константы финального класса. При этом стоит избегать антипаттерн «константный интерфейс» — это плохая практика.

Не забывайте, что REST-assured позволяет выносить параметры пути, например:

Также, если во многих запросах вы используете один и тот же базовый путь, то будет хорошей практикой вынести его в отельную константу и передавать в basePath, например:

То же самое применимо к хосту и порту тестируемого приложения.

ContentType/Accept

Данные заголовки используются практически во всех HTTP-запросах. Авторы REST-assured, понимая это, сделали возможным их установку через вызов специальных методов:

Хорошей практикой будет установить данные заголовки в спецификации или на глобальном уровне. Это повысит читабельность вашего кода.

StatusCode и т.п.

REST-assured предоставляет удобный синтаксис для проведения проверки каждой составляющей HTTP-ответа, однако на практике продолжаешь встречать подобный код:

Используйте спецификации

Дублирование кода — это плохо. Используйте спецификации для уменьшения дублирования. В REST-assured можно создавать спецификации как для запроса, так и для ответа. В спецификацию запроса выносим всё, что может быть продублировано в запросах.

В спецификацию ответа выносим все проверки, которые дублируются от запроса к запросу.

Можно создавать несколько спецификаций для разных типов запросов/ответов и использовать в нужной ситуации.

Не пишите свои костыли для преобразования объектов

Не стоит преобразовывать свои POJO в JSON при помощи Jackson ObjectMapper’а, а потом полученную строку передавать в тело запроса. REST-assured прекрасно справляется с этой задачей. Для этого используется всё тот же Jackson или Gson, в зависимости от того, что находится в classpath. Для преобразования в XML используется JAXB. Исходный формат определяется автоматически по значению Content-Type.

Кроме того REST-assured прекрасно справляется с преобразованием HashMap в JSON и обратно.

Используйте всю мощь Groovy

Сама библиотека REST-assured написана на Groovy и позволяет вам применять различные методы из Groovy к полученному JSON/XML ответу. Например:

Использование методов из Groovy позволяет сильно сократить количество кода, написанного вами для поиска необходимого значения из ответа.

На этом всё, если у вас есть еще советы и примеры пишите их в комментариях.

Источник

Руководство по REST-уверены

Изучите основы программы REST-assured – библиотеки, которая упрощает тестирование и проверку API REST.

1. Введение

КОМПАНИЯ REST-assured была разработана для упрощения тестирования и проверки API REST и в значительной степени зависит от методов тестирования, используемых в динамических языках, таких как Ruby и Groovy.

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

Кроме того, чтобы узнать о более продвинутых случаях использования REST-assured, ознакомьтесь с другими нашими статьями:

Теперь давайте погрузимся в с простым примером.

2. Простой пример теста

Прежде чем мы начнем, давайте гарантируем, что наши тесты имеют следующие статические импорта:

Нам нужно, чтобы тесты были простыми и имели легкий доступ к основным API.

Теперь давайте начнем с простого примера – базовой системы ставок, разоблачающих некоторые данные для игр:

Допустим, это ответ JSON от удара локально развернутых API – http://localhost:8080/events?id=390. :

Давайте теперь использовать REST-уверены, чтобы проверить некоторые интересные особенности ответа JSON:

Итак, что мы сделали здесь – мы проверили, что вызов в конечную точку /события?id-390 отвечает телом, содержащим JSON Струнные чьи leagueId из данные объект 35.

Рассмотрим более интересный пример. Допустим, вы хотели бы проверить, что шансы массив имеет записи с ценами 1.30 и 5.25 :

3. Установка, гарантированная REST

Если вашим любимым инструментом зависимости является Maven, мы добавляем следующую зависимость в пом.xml файл:

4. Анонимная проверка корня JSON

Рассмотрим массив, который состоит из примитивов, а не объектов:

Это называется анонимным корнем JSON, что означает, что он не имеет пары ключевых значений, тем не менее, он по-прежнему действителен JSON данных.

5. Поплавки и парный разряд

Когда мы начинаем использовать РЕСТ-гарантированные для тестирования наших услуг REST, мы должны понимать, что плавающие точечные числа в ответах JSON отображаются на примитивных типах плавать.

Использование поплавок тип не взаимозаменяем с двойной как и во многих сценариях java.

В качестве точки зрения зрения зрения имеется такой ответ:

предположим, что мы запускаем следующий тест на значение ck :

6. Определение метода запроса

Как правило, мы выполняем запрос, вызывая такой метод, как получить (), в соответствии с методом запроса, который мы хотим использовать.

Кроме того, мы также можем указать глагол HTTP, используя запрос () метод :

Приведенный выше пример эквивалентен использованию получить () прямо.

Поместить запрос также следует аналогичному синтаксису, и мы можем указать тело с помощью с () и тело() методика.

Поэтому, чтобы создать новую Нечетные отправив POST просьба:

Нечетные объект, отправленный в тело автоматически преобразуется в JSON. Мы также можем пройти любое Струнные что мы хотим послать в качестве нашего POST тело.

7. Конфигурация значений по умолчанию

Мы можем настроить много значений по умолчанию для тестов:

Здесь мы устанавливаем базовый URI и порт для наших запросов. Помимо этого, мы также можем настроить базовый путь, погладить корень, и аутентификации.

Примечание: мы также можем сбросить на стандартные неувереемые в REST по умолчанию с помощью:

8. Измерение времени отклика

Давайте посмотрим, как мы можем измерить время отклика с помощью время () и timeIn () методы Ответные объект :

Обратите внимание, что:

8.1. Проверка времени отклика

Мы также можем проверить время отклика – в миллисекундах – с помощью простых долго Совпадений:

Если мы хотим проверить время отклика в другой единице времени, то мы будем использовать время () матч с второй ТаймУнит параметр:

9.XML Проверка ответов

Он не только может проверить ответ JSON, он может проверить XML, а также.

Допустим, мы делаем запрос на http://localhost:8080/employees и мы получаем следующий ответ:

Мы можем проверить, что имя это Джейн как так:

Мы также можем проверить, что все значения соответствуют нашим ожидаемым значениям, приковав вместе спички тела так:

Или с помощью сокращенной версии с переменными аргументами:

10. XPath для XML

Мы также можем проверить наши ответы с помощью XPath. Рассмотрим пример ниже, который выполняет матч на имя :

XPath также принимает альтернативный способ запуска равныйВ Совпадений:

11. Подробная информация о тестировании журналов

11.1. Подробная информация о запросе журнала

Во-первых, давайте посмотрим, как войти все детали запроса с помощью журнал ().все() :

Это будет журнал что-то вроде этого:

Чтобы войти только в определенные части запроса, у нас есть журнал () метод в сочетании с парамы (), тело (), загонщики (), печенье (), метод (), путь () например, журнал. ().парамы ().

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

11.2. Подробная информация о регистрации

Аналогичным образом, мы можем войти детали ответа.

В следующем примере мы регистрим только тело ответа:

11.3. Реакция журнала если условие произошло

У нас также есть возможность регистрации ответа только в том случае, если произошла ошибка или код статуса соответствует данному значению:

11.4. Если проверка не удалась

Мы также можем регистрировать как запрос, так и ответ только в том случае, если наша проверка не была смогла:

В этом примере мы хотим проверить, что код статуса 200. Только в том случае, если это не удается, запрос и ответ будут зарегистрированы.

12. Заключение

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

Источник

REST Assured: что мы узнали за пять лет использования инструмента

REST Assured — DSL для тестирования REST-сервисов, который встраивается в тесты на Java. Это решение появилось более девяти лет назад и стало популярным из-за своей простоты и удобного функционала.

В DINS мы написали с ним более 17 тысяч тестов и за пять лет использования столкнулись со множеством «подводных камней», о которых нельзя узнать сразу после импорта библиотеки в проект: статическим контекстом, путаницей в порядке применения фильтров к запросу, трудностями в структурировании теста.

Эта статья — о таких неявных особенностях REST Assured. Их нужно учитывать, если есть шанс, что количество тестов в проекте будет быстро увеличиваться — чтобы потом не пришлось переписывать.

Rest assured что это. yrxijpz m48cyfek2 0czo55bqc. Rest assured что это фото. Rest assured что это-yrxijpz m48cyfek2 0czo55bqc. картинка Rest assured что это. картинка yrxijpz m48cyfek2 0czo55bqc

Что тестируем

DINS участвует в разработке UCaaS-платформы. В том числе мы разрабатываем и тестируем API, который компания RingCentral использует сама и предоставляет сторонним разработчикам.

При разработке любого API важно следить, чтобы он работал корректно, но когда отдаешь его наружу, приходится проверять намного больше кейсов. Поэтому на каждый новый эндпоинт добавляются десятки и сотни тестов. Тесты написаны на Java, в качестве тестового фреймворка выбран TestNG, а для запросов к API используется REST Assured.

Когда REST Assured принесет пользу

Если вашей целью не является досконально протестировать весь API, то проще всего это сделать с REST Assured. Он хорошо подходит для проверки структуры ответов, PVD и smoke-тестов.

Так выглядит простой тест, который будет проверять, что эндпоинт отдает статус 200 OK при обращении к нему:

Реальные тесты обычно больше и сложнее. В запросы добавляются заголовки, куки, авторизация, тело запроса. И если тестируемый API не состоит из десятков уникальных ресурсов, каждый из которых требует особых параметров, вы захотите где-то хранить уже готовые шаблоны, чтобы добавлять их потом к конкретному вызову в тесте.

Для такого в REST Assured существуют:

RequestSpecification и ResponseSpecification

Эти два класса позволяют определить параметры запроса и ожидания от ответа:

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

Получилось, что в вызов добавлены все заголовки, а вот URI внезапно стал localhost — хотя его добавили в первой спецификации.

Это произошло из-за того, что REST Assured по-разному справляется с переопределениями для параметров запроса (с ответом то же самое). Заголовки или фильтры добавляются в список, а потом по очереди применяются. URI может быть только один, поэтому применяется последний заданный. В последней добавленной спецификации его не задали — поэтому REST Assured переопределяет его дефолтным значением (localhost).

Если добавляете к запросу спецификацию — добавляйте одну. Совет кажется очевидным, но когда проект с тестами разрастается, возникают классы-хэлперы и базовые тестовые классы, внутри них появляются before-методы. Уследить за тем, что на самом деле происходит с вашим запросом, становится сложно, особенно если тесты пишет сразу несколько человек.

Базовая конфигурация REST Assured

Другой способ шаблонизировать запросы в REST Assured — настроить базовую конфигурацию и определить статические поля класса RestAssured:

Значения будут автоматически добавляться к запросу каждый раз. Конфигурация сочетается с аннотациями @BeforeMethod в TestNG и @BeforeEach в JUnit –– так можно быть уверенными, что каждый запущенный тест будет начинаться с одними и теми же параметрами.

Тем не менее, конфигурация станет потенциальным источником проблем, ведь она является статической.

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

Фильтры REST Assured

Фильтры изменяют как запросы перед отправкой, так и ответы перед проверкой на соответствие заданным ожиданиям. Пример применения — добавление логирования, или авторизации:

Порядок фильтров имеет значение. Эти два запроса приведут к разным логам: в первом будет указан авторизационный заголовок, во втором — нет. При этом заголовок будет добавлен в оба запроса — просто в первом случае REST Assured сначала добавит авторизацию до того, как залогировать, а во втором — наоборот.

Конечно, здесь можно запутаться и случайно выставить двум фильтрам одинаковый приоритет, например, в 999. Тогда первым к запросу будет применен тот, который был добавлен раньше.

Не только фильтры

Как сделать авторизацию через фильтры, показано выше. Но кроме этого способа в REST Assured существует и другой, через AuthenticationScheme :

Это устаревший способ. Вместо него стоит выбрать тот, который показан выше. Причины две:

Проблема с зависимостями

Документация к REST Assured указывает, что для использования Oauth1 или Oauth2 (указывая токен в качестве query-параметра) авторизации необходимо добавить в зависимости Scribe. Однако импорт последней версии вам не поможет — у вас возникнет ошибка, описанная в одной из открытых проблем. Решить ее можно только импортом старой версии библиотеки, 2.5.3. Однако в этом случае вы наткнетесь на другую проблему.

Вообще никакая другая версия Scribe не работает с Oauth2 REST Assured версии 3.0.3 и выше (и недавний выход 4.0.0 это не исправил).

Логирование не работает

Фильтры применяются к запросам в определенном порядке. А AuthenticationScheme применяется после них. А значит, будет трудно обнаружить проблему с авторизацией в тесте — она же не залогируется.

Еще о синтаксисе REST Assured

Большое количество тестов обычно значит, что они еще и сложные. А если API является основным предметом тестирования, и нужно проверить не просто поля json’a, а бизнес-логику, то с REST Assured тест превращается в простыню:

Этот тест проверяет, что, когда мы кормим Куки-монстра, мы правильно подсчитываем, сколько печенек ему дали, и указываем это в истории. Но с первого взгляда это нельзя понять — все запросы выглядят одинаково, и неясно, где заканчивается подготовка данных через API, а где посылается тестируемый запрос.

И вызывать в тесте:

Хорошо, когда такие классы-хэлперы универсальны, чтобы вызов одного метода можно было встроить в большое количество тестов — тогда их вообще можно вынести в отдельную библиотеку: вдруг потребуется в какой-то момент вызвать метод в другом проекте. Только при этом придется убрать всю проверку ответа, которую может сделать Rest Assured — все-таки в ответ на один и тот же запрос часто могут вернуться совсем разные данные.

Заключение

REST Assured — это библиотека для тестирования. Она умеет делать две вещи: посылать запросы и проверять ответы. Если мы пытаемся вынести ее из тестов и убрать всю валидацию, то она превращается в HTTP-клиент.

Если вам предстоит написать большое количество тестов и в дальнейшем их поддерживать – задумайтесь, нужен ли в них HTTP-клиент с громоздким синтаксисом, статической конфигурацией, путаницей в порядке применения фильтров и спецификаций и логированием, которое можно легко сломать? Может быть, девять лет назад REST Assured был самым удобным инструментом, но за это время появились альтернативы, — Retrofit, Feign, Unirest и т.д., — у которых нет таких особенностей.

Большинство проблем, которые описаны в статье, проявляют себя в крупных проектах. Если вам нужно быстро написать пару тестов и навсегда про них забыть, а Retrofit не нравится, REST Assured — лучший вариант.

Если вы уже пишете тесты с использованием REST Assured, не обязательно бросаться все переписывать. Если они стабильные и быстрые, это потратит больше вашего времени, чем принесет практической пользы. Если нет — REST Assured не ваша основная проблема.

Каждый день количество тестов, написанных в DINS для API RingCentral, становится все больше, и они по-прежнему используют REST Assured. Количество времени, которое придется потратить, чтобы перейти на другой HTTP-клиент хотя бы в новых тестах, слишком велико, а созданные классы-хэлперы и методы, настраивающие конфигурацию тестов, решают большинство проблем. В этом случае сохранить цельность проекта с тестами важнее, чем использовать самый красивый и модный клиент. REST Assured, несмотря на свои недостатки, выполняет свою главную работу.

Источник

Тестирование RESTful API при помощи Serenity и REST Assured на Kotlin

Rest assured что это. 1* L96KJRU. Rest assured что это фото. Rest assured что это-1* L96KJRU. картинка Rest assured что это. картинка 1* L96KJRU

В этой заметке попробуем разобраться с тем, как на языке Kotlin написать тесты для проверки RESTful API приложения. В интернете полно статей про то, как писать подобные тесты на Java и Serenity, мы же воспользуемся Kotlin.

Какие технологии и библиотеки будем использовать:

Что такое Serenity BDD?

Краткий список возможностей:

Что такое Rest Assured?

Rest Assured — это Java-библиотека для тестирования RESTful API. Код, написанный с помощью этой библиотеки, имеет простой и понятный синтаксис. В тесте можно выполнить запрос к API буквально в одну строчку кода и при помощи DSL синтаксиса проверять полученный результат.

Подготовка

В оригинальной статье в качестве сборщика проекта используется maven, мы же воспользуемся Gradle. (https://gradle.org/maven-vs-gradle/)

Перед тем как приступить к написанию тестов, необходимо:

Настройка проекта в IDE

Создаем новый проект (Create new project), выбираем Gradle — Kotlin (Java):

Rest assured что это. 1*LuSoQZ9SSK z7CE phXG0Q. Rest assured что это фото. Rest assured что это-1*LuSoQZ9SSK z7CE phXG0Q. картинка Rest assured что это. картинка 1*LuSoQZ9SSK z7CE phXG0Q

Заполняем необходимые поля:

Rest assured что это. 1*1jtifctNm0FrT9nCQvt1TQ. Rest assured что это фото. Rest assured что это-1*1jtifctNm0FrT9nCQvt1TQ. картинка Rest assured что это. картинка 1*1jtifctNm0FrT9nCQvt1TQ

Указываем путь к gradle. У меня в MacOS он установлен через homebrew и Idea не находит его автоматически, поэтому я указываю путь руками: /usr/local/Cellar/gradle/4.2.1/libexec:

Rest assured что это. 1*71B30YU1tYiLkmtMEps. Rest assured что это фото. Rest assured что это-1*71B30YU1tYiLkmtMEps. картинка Rest assured что это. картинка 1*71B30YU1tYiLkmtMEps

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

Настройка зависимостей

Нам необходимо подключить следующие библиотеки:

Вносим изменения в build.gradle. Жирным шрифтом отмечены сделанные изменения относительно той версии, которая получилась после создания проекта.

Вот и вся настройка. Перейдем к написанию тестов.

Первый тест

Для написания тестов нам необходим какой-либо веб-сайт, работающий по RESTful API. Воспользуемся http://www.groupkt.com/, который предоставляет сервисы, с помощью которых, например, можно выполнить поиск страны по коду ISO при помощи метода API /country/get/iso2code/

Ниже приведён ответ сервера в формате JSON:

В папке serenity-rest-assured-automation/src/test/kotlin/tests/ создаем файл GroupktAPITest.kt и добавляем в него следующий текст:

Тест делает запрос на адрес http://services.groupkt.com/country/get/iso2code/RU и проверяет, что в ответе в поле RestResponse.result.name содержится значение “ Russian Federation”

Кстати, код взят из оригинальной статьи и автоматически конвертирован из Java в Kotlin при его вставке в Idea. При этом некоторые инструкции (when, is) были экранированы при помощи символа «, так как они являются ключевыми словами в Kotlin.

Попробуем запустить тест.

Запуск тестов

Тесты можно запускать двумя способами: через JUnit и через Gradle.

Чтобы запустить тесты через JUnit, в Idea надо навести мышь на значок запуска слева от строки, в которой объявлен класс и выбрать Run Test:

Rest assured что это. 1*9pL P0I1drAu5RzR7XchsA. Rest assured что это фото. Rest assured что это-1*9pL P0I1drAu5RzR7XchsA. картинка Rest assured что это. картинка 1*9pL P0I1drAu5RzR7XchsA

После прогона тестов (в нашем случае одного теста), внизу окна Idea отобразится результат тестирования:

Rest assured что это. 1*XcAikiRXukjK5qDH ecs1A. Rest assured что это фото. Rest assured что это-1*XcAikiRXukjK5qDH ecs1A. картинка Rest assured что это. картинка 1*XcAikiRXukjK5qDH ecs1A

Теперь настроим запуск тестов через Gradle и в дальнейшем будем пользоваться этим способом запуска.

3. Выбираем Gradle project из выпадающего списка

Rest assured что это. 1*SBjoVDoZ6RLD32tGbQX Dw. Rest assured что это фото. Rest assured что это-1*SBjoVDoZ6RLD32tGbQX Dw. картинка Rest assured что это. картинка 1*SBjoVDoZ6RLD32tGbQX Dw

Сохраняем изменения и запускаем тест при помощи Gradle. После прогона увидим результат:

Rest assured что это. 1*dX0S sPOK4BrO23upRgsCw. Rest assured что это фото. Rest assured что это-1*dX0S sPOK4BrO23upRgsCw. картинка Rest assured что это. картинка 1*dX0S sPOK4BrO23upRgsCw

Посмотрим, как выглядит упавший тест. Для этого поменяем в тесте код страны и запустим тест заново:

Rest assured что это. 1*p0mVZCIXjm9RSV3U0cpNuQ. Rest assured что это фото. Rest assured что это-1*p0mVZCIXjm9RSV3U0cpNuQ. картинка Rest assured что это. картинка 1*p0mVZCIXjm9RSV3U0cpNuQ

В логе можно увидеть причину падения:

Возвращаем код страны обратно на RU.

Теперь добавим несколько тестов, чтобы проверить поиск других стран.

Добавляем еще тесты

Добавим поиск США и Индии по их ISO-кодам:

Полную версию можно посмотреть тут.

Если посмотреть на код, то можно увидеть, что в нём повторяются одни и те же инструкции для каждого теста:

Чтобы избавиться от повторяющихся фрагментов кода, в нашем случае можно:

Первый вариант хотя и является самым очевидным, но он не такой гибкий, как остальные. Что если нам понадобится добавить еще пару проверок для других тестов?

Рассмотрим второй вариант.

В Serenity тесты принято разбивать на небольшие блоки кода, которые затем можно использовать повторно при необходимости. Эти блоки называются “шагами”. Применяя подобный принцип на практике, можно перейти от использования технических формулировок (“код http-ответа = 200” или “выполни http запрос”) к выражениям, понятным обычному человеку: “действие было успешным” или “выполни поиск”. Поэтому всегда лучше начинать с реализации маленьких шагов, описывающих какие-либо действия, а затем использовать их для создания более сложных шагов и сценариев. В общем, чтобы сделать тесты легко поддерживаемыми необходимо использовать принцип DRY (Don’t Repeat Yourself)

Выносим шаги в отдельный пакет

Создаем новый файл src/test/kotlin/steps/CountriesSearchSteps.kt :

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

Класс и его методы объявлены как open. Это необходимо, чтобы Serenity мог корректно сформировать отчёт, детализируя его пошагово.

Запускаем прогон тестов. Он должен завершиться успешно.

Теперь мы можем спокойно добавить дополнительный шаг и применить его в одном тесте.

Параметризованные тесты

Библиотека Serenity имеет встроенные механизмы для создания параметризованных тестов. Тестовые данные можно подгружать из csv-файла или задать в коде. Рассмотрим второй вариант.

Что необходимо сделать:

В итоге Serenity при инициализации тестового класса будет передавать в него очередную порцию тестовых данных и запускать все тесты внутри класса. К тестовым данным можно получить доступ через атрибуты класса.

Отчёты

Чтобы формировался отчет, необходимо использовать команду aggregate, которую мы добавили ранее при настройке запуска тестов через Gradle.

Сформированный отчет можно найти в каталоге target/site/serenity/. Переходим в этот каталог и открываем файл index.html:

Rest assured что это. 1*LatvyrDRiIa pANPkqcJ9g. Rest assured что это фото. Rest assured что это-1*LatvyrDRiIa pANPkqcJ9g. картинка Rest assured что это. картинка 1*LatvyrDRiIa pANPkqcJ9g

В отчете можно увидеть шаги, которые выполнялись в каждом тесте и даже результаты http- запросов.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *