{"id":19411617,"url":"https://github.com/levvolkov/postmanecho","last_synced_at":"2026-05-06T08:36:36.122Z","repository":{"id":231538905,"uuid":"782000535","full_name":"levvolkov/postmanEcho","owner":"levvolkov","description":"2.2 «Тестирование API, Continuous Integration»   ","archived":false,"fork":false,"pushed_at":"2024-04-04T13:54:57.000Z","size":9736,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-07T10:47:36.512Z","etag":null,"topics":["automated-testing","gradle","java-ci-with-gradle","json-schema","postman-echo"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/levvolkov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-04T12:56:10.000Z","updated_at":"2025-02-02T16:00:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"969bc8ab-d848-49a2-8036-69d25a9e5748","html_url":"https://github.com/levvolkov/postmanEcho","commit_stats":null,"previous_names":["levvolkov/postmanecho"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/levvolkov/postmanEcho","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levvolkov%2FpostmanEcho","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levvolkov%2FpostmanEcho/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levvolkov%2FpostmanEcho/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levvolkov%2FpostmanEcho/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/levvolkov","download_url":"https://codeload.github.com/levvolkov/postmanEcho/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levvolkov%2FpostmanEcho/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32685031,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T08:33:17.875Z","status":"ssl_error","status_checked_at":"2026-05-06T08:33:17.221Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automated-testing","gradle","java-ci-with-gradle","json-schema","postman-echo"],"created_at":"2024-11-10T12:22:23.028Z","updated_at":"2026-05-06T08:36:36.107Z","avatar_url":"https://github.com/levvolkov.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Java CI with Gradle](https://github.com/LevVolkov/PostmanEcho/actions/workflows/gradle.yml/badge.svg)](https://github.com/LevVolkov/PostmanEcho/actions/workflows/gradle.yml)\n\n# 2.2 Домашнее задание к занятию «1.2. Тестирование API, CI » Задача 3.\n\nВ качестве результата пришлите ссылку на ваш GitHub-проект в личном кабинете студента на сайте [netology.ru](https://netology.ru).\n\nПервые две задачи этого занятия нужно делать в одном репозитории.\n\n**Важно**: если у вас что-то не получилось, то оформляйте issue [по установленным правилам](https://github.com/netology-code/aqa-homeworks/blob/master/report-requirements.md).\n\n**Важно**: не делайте ДЗ всех занятий в одном репозитории. Иначе вам потом придётся достаточно сложно подключать системы Continuous integration.\n\n## Как сдавать задачи\n\n1. Инициализируйте на своём компьютере пустой Git-репозиторий.\n1. Добавьте в него готовый файл [.gitignore](https://github.com/netology-code/aqa-homeworks/blob/master/.gitignore).\n1. Добавьте в этот же каталог код вашего приложения, код проекта для решения первой и второй задачи вы можете найти в [репозитории с учебным кодом](https://github.com/netology-code/aqa-code/tree/master/api-ci/rest).\n1. Сделайте необходимые коммиты.\n1. Создайте публичный репозиторий на GitHub и свяжите свой локальный репозиторий с удалённым.\n1. Сделайте пуш — удостоверьтесь, что ваш код появился на GitHub.\n1. Выполните интеграцию проекта с Github Actions ([инструкция](https://github.com/netology-code/aqa-homeworks/tree/master/github-actions-integration)) или Appveyor ([инструкция](https://github.com/netology-code/aqa-homeworks/tree/master/api-ci#appveyor)) на выбор, удостоверьтесь что автотесты в CI выполняются.\n1. Ссылку на ваш проект отправьте в личном кабинете на сайте [netology.ru](https://netology.ru).\n1. Задачи, отмеченные, как необязательные, можно не сдавать, это не повлияет на получение зачёта.\n\nВ качестве примера можете посмотреть на этот [проект](https://github.com/netology-code-samples/aqa-ci-demo). Ваш по структуре должен выглядеть так же.\n\n## Информация по учебным JAR\n\nJAR файл учебного сервиса поместите в папку artifacts репозитория проекта. Для запуска учебного сервиса, находясь в папке проекта, выполните в терминале команду        \n```\njava -jar ./artifacts/app-mbank.jar\n```\n\nВажно: если вы работаете на Windows с турецкой локалью и при запуске учебных приложений получаете Exception вида:\n```\nCaused by: java.lang.NoSuchFieldException: wrıteHandlerReference (тут не английская i, а именно ı или ?)\n  at java.lang.Class.getDeclaredField(Unknown Source)\n  at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1.run(Unknown Source)\n  at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1.run(Unknown Source)\n  at java.security.AccessController.doPrivileged(Native Method)\n  ... 21 more\n```\n\nтогда вам все JAR нужно будет запускать командой:\n```\njava -Duser.language=en -Duser.country=US -jar ./artifacts/app-mbank.jar\n```\n\nЧасть `./artifacts/app-mbank.jar` может меняться в зависимости от имени файла учебного сервиса и папки с ним, но первая часть для вас во всех ДЗ при запуске на вашем ПК будет именно такой.\n\n## Задача №1: настройка CI\n\nНапоминаем, CI — это чаще всего отдельная система — сервер, набор серверов, облако — в которой ваш код и ваши автотесты собираются в автоматическом режиме без вашего непосредственного участия. Вы лишь настраиваете CI для того, чтобы при возникновении определённых событий, например, push в репозиторий, стартовал процесс сборки и прогона тестов.\n\nДетальнее про CI вы можете узнать, погуглив «Continuous integration», «Jenkins», «GitLab CI», «AppVeyor», «Travis», «CircleCI», «GitHub Actions».\n\nПолучить код проекта для решения первой и второй задач можно выполнив следующие шаги:   \n1. Клонируйте [репозиторий с примерами учебного кода](https://github.com/netology-code/aqa-code/tree/master) во временную папку на локальный компьютер       \n2. Из папки /api-ci/rest локальной копии репозитория aqa-code копируйте содержимое в папку проекта с решением задачи\n      \n**Важно**: иногда можно настроить CI так, что там **всегда будет success** 😈! Не забывайте убедиться, что в CI сборка действительно падает, если вы запушите в GitHub падающий тест.\n\n\u003cdetails\u003e\n  \u003csummary\u003eПодсказка\u003c/summary\u003e\n  \n  Возможно, это как-то связано с файлом gradlew и правами доступа на него. Для добавления прав на запуск файла gradlew, добавьте в CI исполнение команды `chmod +x gradlew` перед тем как использовать этот файл как команду для работы с гредлом. \n\u003c/details\u003e\n\nОбщая схема работы выглядит следующим образом: CI должен запустить целевой сервис, который вы и тестируете, в фоновом режиме и ваши автотесты. Для этого мы будем на этот раз использовать возможности Bash.\n\nДля того чтобы запустить целевой сервис, есть несколько вариантов, самый простой из которых — положить JAR-файл прямо в ваш репозиторий. Когда AppVeyor будет выкачивать исходники автотестов, он выкачает и ваш сервис.\n\nКонечно, вы должны понимать, что в реальной жизни артефакты (собранный целевой сервис) хранятся в специальных системах, и процесс выкачивания будет зависеть от того, где и как хранится артефакт.\n\nВаш целевой сервис (SUT — System under test), расположен в файле [app-mbank.jar](app-mbank.jar), этот же файл используется в примерах на лекции. Вам нужно его положить в каталог `artifacts` вашего проекта, который необходимо создать.\n\nПоскольку файлы с расширением `.jar` находятся в списках `.gitignore`, вам нужно принудительно заставить Git следить за ними: `git add -f artifacts/app-mbank.jar`.\n\nПосле чего сделать `git push`. Обязательно удостоверьтесь, что файл попал в репозиторий.\n\n### AppVeyor\n\n[AppVeyor](https://www.appveyor.com) — одна из платформ, предоставляющих функциональность Continuous integration. В базовом варианте она бесплатна.\n\n#### Шаг 0. Конфигурация как код\n\nПоскольку вручную настраивать каждый проект в системе Continuous integration — лишняя трата времени, мы будем хранить всю конфигурацию для AppVeyor в специальном файле с названием `.appveyor.yml`.\n\nВажно: внимательно посмотрите на структуру деморепозитория из ваших лекций. Большинство инструментов используют подход «Configuration by exception», то есть конфигурируется только то, что не соответствует настройкам по умолчанию. Поэтому у вас всего два пути: либо использовать настройки по умолчанию и писать как можно меньше конфигурации, либо идти против системы и писать много конфигурации, а потом ещё и отлаживать её.\n\nФайл этот должен храниться в самом репозитории на GitHub, тогда AppVeyor будет автоматически подхватывать настройки из него:\n\n![](https://i.imgur.com/Gg7B961.png)\n\nYaml — формат данных, используемый многими системами для хранения конфигурации.\n\nСсылки:\n* [Wikipedia](https://en.wikipedia.org/wiki/YAML),\n* [спецификация](https://yaml.org/spec/1.2/spec.html).\n\nСтранички на Wikipedia достаточно для понимания базовых конструкций языка.\n\nAppVeyor предлагает вам два вида серверов, на которых можно проводить сборку вашего приложения: под управлением Windows или под управлением Linux. Можно организовать сборку под несколькими сразу, но для упрощения мы пока остановимся только на одной ОС для каждого вашего проекта.\n\n##### Linux Config\n\n```yaml\nimage: Ubuntu2004  # образ для сборки\n\nstack: jdk 11  # версия JDK\n\nbranches:\n  only:\n    - master  # ветка git\n\nbuild: off  # будем использовать свой скрипт сборки\n\ninstall:\n  # запускаем SUT (\u0026 означает, что в фоновом режиме не блокируем терминал для запуска тестов)\n  - java -jar ./artifacts/app-mbank.jar \u0026\n  - chmod +x gradlew\n\nbuild_script:\n  - ./gradlew test --info  # запускаем тест, флаг --info позволяет выводить больше информации\n```\n\nЕстественно, у вас должен возникнуть вопрос, а что будет, если SUT не успеет стартовать к моменту запуска автотестов?\n\nТогда ваши тесты упадут. Что с этим делать и как классифицировать подобные случаи, мы поговорим на следующих лекциях.\n\nНапоминаем, ваш `build.gradle` должен выглядеть вот так:\n```groovy\nplugins {\n    id 'java'\n}\n\ngroup 'ru.netology'\nversion '1.0-SNAPSHOT'\n\nsourceCompatibility = 11\ncompileJava.options.encoding = 'UTF-8'\ncompileTestJava.options.encoding = 'UTF-8'\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    testImplementation 'io.rest-assured:rest-assured:5.3.1'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation 'io.rest-assured:json-schema-validator:4.3.1'\n}\n\ntest {\n    useJUnitPlatform()\n}\n```\n\n#### Шаг 1. Регистрация\n\n![](https://i.imgur.com/Rugmz7D.png)\n\n#### Шаг 2. Регистрация через GitHub\n\nAppVeyor предоставляет бесплатный тарифный план для публичных репозиториев GitHub, авторизация — также через GitHub:\n\n![](https://i.imgur.com/jXvftMb.png)\n\n#### Шаг 3. Разрешение доступа\n\nПри подключении необходимо разрешить AppVeyor получать уведомления:\n\n![](https://i.imgur.com/2Fvcj96.png)\n\n#### Шаг 4. Создание проекта\n\nПосле авторизации станет доступной панель управления, где можно создать новый проект:\n\n![](https://i.imgur.com/wUBKbYY.png)\n\nАвторизуйте AppVeyor в качестве OAuth App:\n\n![](https://i.imgur.com/oQadLLj.png)\n\nЭто даст возможность приложению получать уведомления о ваших `push` в репозиторий, модификации и т. д.\n\n![](https://i.imgur.com/2jwH6Sa.png)\n\nДетальнее об OAuth вы можете прочитать на:\n* https://oauth.net/2/,\n* https://auth0.com/docs/protocols/oauth2.\n\n#### Шаг 5. Выбор репозитория\n\nПосле авторизации достаточно будет нажать кнопку `ADD` напротив необходимого репозитория:\n\n![](https://i.imgur.com/4VQME6j.png)\n\nПосле настройки всего процесса каждый `push` в ветку `master` GitHub-репозитория будет приводить к запуску сборки на AppVeyor.\n\n#### Шаг 6. Status badge\n\nНа странице `Settings` — `Badges` AppVeyor предлагает код для бейджика статуса вашего проекта:\n\n![](https://i.imgur.com/DECtZjg.png)\n\nЭтот badge необходимо разместить в файле `README.md` для отображения текущего статуса вашего проекта:\n\n![](https://i.imgur.com/V9cOeJO.png)\n\n**Важно: убедитесь, что вы не скопировали бейджик с другого проекта. За такую хитрость ДЗ будет отправляться на доработку.**\n\n## Задача №2: JSON Schema\n\nJSON Schema предлагает нам инструмент валидации JSON-документов. С описанием вы можете познакомиться по [адресу](https://json-schema.org/understanding-json-schema).\n\nКак строится схема: \n```js\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema\", // версия схемы: https://json-schema.org/understanding-json-schema/reference/schema.html\n  \"type\": \"array\", // тип корневого элемента: https://json-schema.org/understanding-json-schema/reference/type.html\n  \"items\": { // какие элементы допустимы внутри массива: https://json-schema.org/understanding-json-schema/reference/array.html#items\n    \"type\": \"object\", // должны быть объектами: https://json-schema.org/understanding-json-schema/reference/object.html\n    \"required\": [ // должны содержать следующие поля: https://json-schema.org/understanding-json-schema/reference/object.html#required-properties\n      \"id\",\n      \"name\",\n      \"number\",\n      \"balance\",\n      \"currency\"\n    ],\n    \"additionalProperties\": false, // дополнительных полей быть не должно \n    \"properties\": { // описание полей: https://json-schema.org/understanding-json-schema/reference/object.html#properties\n      \"id\": {\n        \"type\": \"integer\" // целое число: https://json-schema.org/understanding-json-schema/reference/numeric.html#integer\n      },\n      \"name\": {\n        \"type\": \"string\", // строка: https://json-schema.org/understanding-json-schema/reference/string.html\n        \"minLength\": 1 // минимальная длина — 1: https://json-schema.org/understanding-json-schema/reference/string.html#length\n      },\n      \"number\": {\n        \"type\": \"string\", // строка: https://json-schema.org/understanding-json-schema/reference/string.html\n        \"pattern\": \"^•• \\\\d{4}$\" // соответствует регулярному выражению: https://json-schema.org/understanding-json-schema/reference/string.html#regular-expressions\n      },\n      \"balance\": {\n        \"type\": \"integer\" // целое число: https://json-schema.org/understanding-json-schema/reference/numeric.html#integer\n      },\n      \"currency\": {\n        \"type\": \"string\" // строка: https://json-schema.org/understanding-json-schema/reference/string.html\n      }\n    }\n  }\n}\n```\n\nНачнем с изучения проекта, проверим необходимые условия для его реализации.   \n\nЧто нужно сделать:    \n\n#### Шаг 1. Зависимости проекта\n\n```groovy\ndependencies {\n    testImplementation 'io.rest-assured:rest-assured:5.3.1'\n    testImplementation 'io.rest-assured:json-schema-validator:4.3.1'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'\n}\n```\n\n#### Шаг 2. JSON схема    \n\nВ каталоге `resources` в `src/test` и должен лежать файл схемы.    \n\n![](https://github.com/netology-code/aqa-homeworks/blob/master/api-ci/)\n\n#### Шаг 3. Проверка ответа сервиса на соответсвие схеме    \n\nОдин из классов проекта должен содержать операцию проверки соответствия ответа схеме. \n\n```java\n      // код теста\n      .then()\n          .statusCode(200)\n          .body(matchesJsonSchemaInClasspath(\"accounts.schema.json\"));\n```\n\nВ тестовом классе должен быть импорт статического метода        \n\n```java\nimport static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;\n```\n\nУдостоверьтесь, что тесты проходят при соответствии ответа схеме и падают, если вы поменяете что-то в схеме, например, тип для `id`.\n\n#### Шаг 4. Доработать схему\n\nИзучите документацию на тип [`object`](https://json-schema.org/understanding-json-schema/reference/object.html) и найдите способ валидации значения поля на два из возможных значений: «RUB» или «USD».\n\nДоработайте схему соответствующим образом, удостоверьтесь, что тесты проходят, в том числе в CI.\n\nПоменяйте «RUB» на «RUR» в схеме и удостоверьтесь, что тесты падают, в том числе в CI.\n\nПришлите на проверку ссылку на ваш репозиторий. Удостоверьтесь, что в истории сборки были как success, так и fail, иначе будет не видно, как вы проверяли, что сборка падает в CI.\n\n## Задача №3: Postman Echo\n\n**Важно**: эту задачу нужно выполнять в отдельном репозитории.\n\nВ этой задаче мы сэмулируем ситуацию, в которой SUT уже запущен, а мы из теста просто обращаемся к нему.\n\nЕсть специальный сервис, предназначенный для тестирования HTTP-запросов. Называется он [Postman Echo](https://docs.postman-echo.com). Никогда не тестируйте автоматизированными средствами веб-сервисы, если у вас нет на это письменного разрешения или если веб-сервисы специально не предназначены для этого.\n\nМы можем отправлять туда запросы и получать ответы.\n\nС GET-запросами мы немного потренировались, теперь нас будут интересовать POST-запросы, а именно отправка тела запроса:\n\n```java\n// Given - When - Then\n// Предусловия\ngiven()\n  .baseUri(\"https://postman-echo.com\")\n  .body(\"some data\") // отправляемые данные (заголовки и query можно выставлять аналогично)\n// Выполняемые действия\n.when()\n  .post(\"/post\")\n// Проверки\n.then()\n  .statusCode(200)\n  .body(/* --\u003e ваша проверка здесь \u003c-- */)\n;\n```\n\nЧто нужно сделать:\n1. Создайте новый проект на базе Gradle.\n2. Добавьте необходимые зависимости. Если вы не пишите схему, то только REST assured.\n3. Напишите тест, взяв сам запрос из кода выше.\n4. Изучите ответ и напишите JSONPath-выражение вместо строк `/* --\u003e ваша проверка здесь \u003c--*/`, которое проверит, что в нужном поле хранятся отправленные вами данные. Обратите внимание, теперь у вас не массив, а объект.\n\nМожете воспользоваться сервисами [jsonpath-tester](https://extendsclass.com/jsonpath-tester.html) или [jsonpath](https://jsonpath.com/) для быстрой проверки выражений. Описание синтаксиса [тут](https://testerslittlehelper.wordpress.com/2019/01/20/jsonpath-in-rest-assured/) и [тут](https://github.com/rest-assured/rest-assured/wiki/Usage#json-using-jsonpath).\n\nУдостоверьтесь, что если вы будете использовать неверное выражение, то тесты упадут, в том числе и в CI.\n\nОбратите внимание: если вам приходит вот такой объект:\n```json\n{\n    \"data\": \"some value\"\n}\n```\n\nто обратиться к нему с помощью JSONPath можно вот так: `data`, например: `.body(\"data\", equalTo(\"some value\"))`. То есть обращение к полю верхнеуровневого объекта, который называется безымянным, идёт без точки. В примере на лекциях у нас был массив и мы сразу обращались `[0]` — то же самое.\n\nЕсли соберётесь отправлять текст не на латинице, то вам нужно будет выставлять кодировку, например, UTF-8:\n```java\ngiven()\n  .baseUri(\"https://postman-echo.com\")\n  .contentType(\"text/plain; charset=UTF-8\")\n  .body(\"some data\")\n.when()\n  .post(\"/post\")\n.then()\n  .statusCode(200)\n  .body(/* --\u003e ваша проверка здесь \u003c-- */)\n;\n```\n\nПришлите на проверку ссылку на ваш репозиторий. Удостоверьтесь, что в истории сборки были как success, так и fail, иначе будет не видно, как вы проверяли, что сборка падает в CI.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevvolkov%2Fpostmanecho","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flevvolkov%2Fpostmanecho","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevvolkov%2Fpostmanecho/lists"}