{"id":483251,"date":"2026-06-11T05:41:17","date_gmt":"2026-06-11T05:41:17","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=483251"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=483251","title":{"rendered":"\u0421\u0442\u0430\u0432\u043a\u0430 \u043d\u0430 API-\u0441\u043b\u043e\u0439 \u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u0445: \u043a\u0430\u043a \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u0438\u0442\u044c UI \u0438 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>\u0414\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440<\/h3>\n<p>\u042d\u0442\u043e \u043d\u0435 \u0441\u0435\u0440\u0435\u0431\u0440\u044f\u043d\u0430\u044f \u043f\u0443\u043b\u044f \u0438 \u043d\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u0433\u043c\u0430.<\/p>\n<p>API-first \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b. \u041e\u043d \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0431\u044b\u043b\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 API, \u0430 UI-\u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0447\u0430\u043b\u0438 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0448\u0435\u0432\u043b\u0435 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0438\u0436\u0435.<\/p>\n<p>\u041c\u044b \u043d\u0435 \u043f\u0440\u0438\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 UI-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. UI \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d. \u0412\u043e\u043f\u0440\u043e\u0441 \u043d\u0435 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b API-\u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438, \u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c:<\/p>\n<ul>\n<li>\n<p>API \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e, \u043f\u0440\u0430\u0432\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>UI \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435;<\/p>\n<\/li>\n<li>\n<p>E2E \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0445 \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0442\u043e \u0432\u0430\u0441 \u0436\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435<\/h3>\n<ul>\n<li>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c UI-heavy \u043f\u043e\u0434\u0445\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API-first \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0430 Java, REST Assured \u0438 JUnit 5.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b: API clients, builders, response wrappers, steps, providers, base-\u043a\u043b\u0430\u0441\u0441\u044b.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a API \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0433\u0440\u0443\u0436\u0430\u0442\u044c UI \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f API-\u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0440\u0430\u0449\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044f API \u0438 UI-\u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u0430 \u0432\u0440\u0435\u043c\u044f CI.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0438 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u044b.<\/p>\n<\/li>\n<\/ul>\n<h3>\u041a\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b<\/h3>\n<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d QA Automation \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0440\u043e\u044f\u0442 \u0438\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 Java-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0433\u0434\u0430:<\/p>\n<ul>\n<li>\n<p>UI-\u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0433\u043e\u043d \u0432 CI \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043e\u043b\u0433\u043e;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0430\u0434\u0435\u043d\u0438\u0435 UI-\u0442\u0435\u0441\u0442\u0430 \u0441\u043b\u043e\u0436\u043d\u043e \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c;<\/p>\n<\/li>\n<li>\n<p>\u0431\u0438\u0437\u043d\u0435\u0441-\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0434\u043b\u0438\u043d\u043d\u044b\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043b\u043e\u0436\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441;<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0445\u043e\u0447\u0435\u0442 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c, \u043d\u043e \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435: \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 API-first \u043f\u043e\u0434\u0445\u043e\u0434 \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438<\/h3>\n<p>API-first \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043c\u0430\u0441\u0441\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0430 \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e \u201c\u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0447\u0435\u0440\u0435\u0437 API\u201d. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0433\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435:<\/p>\n<ul>\n<li>\n<p>80% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 API-\u0443\u0440\u043e\u0432\u0435\u043d\u044c: \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u043f\u0440\u0430\u0432\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b;<\/p>\n<\/li>\n<li>\n<p>15% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 UI-\u0443\u0440\u043e\u0432\u0435\u043d\u044c: \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u0444\u043e\u0440\u043c\u044b, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435;<\/p>\n<\/li>\n<li>\n<p>5% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 E2E: \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u043a \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043a\u043e\u043d. \u0414\u043b\u044f \u043d\u0430\u0441 \u043e\u043d\u043e \u0441\u0442\u0430\u043b\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u043c: \u0435\u0441\u043b\u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0434\u0435\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 API-\u0443\u0440\u043e\u0432\u043d\u0435, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0442\u0430\u0449\u0438\u0442\u044c \u0435\u0433\u043e \u0432 UI \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0442\u0430\u043a \u201c\u0432\u0438\u0434\u043d\u0435\u0435\u201d.<\/p>\n<h3>\u0411\u043e\u043b\u044c UI-heavy \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e, UI-\u0442\u0435\u0441\u0442\u044b \u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0441\u0430\u043c\u044b\u043c \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c: \u043e\u043d\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u043e\u043c. \u041d\u043e \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u2014 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c. UI-\u0442\u0435\u0441\u0442\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u043d\u0430\u0439\u0442\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0438 \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0440\u0435\u0430\u043a\u0446\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430. \u0415\u0441\u043b\u0438 \u0442\u0430\u043a\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u043c\u043d\u043e\u0433\u043e, CI \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c.<\/p>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u2014 \u0445\u0440\u0443\u043f\u043a\u043e\u0441\u0442\u044c. UI-\u0442\u0435\u0441\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438, \u043d\u043e \u0438 \u043e\u0442 \u043b\u043e\u043a\u0430\u0442\u043e\u0440\u043e\u0432, \u0432\u0435\u0440\u0441\u0442\u043a\u0438, \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a, \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>\u0422\u0440\u0435\u0442\u044c\u044f \u2014 \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430. \u0415\u0441\u043b\u0438 API-\u0442\u0435\u0441\u0442 \u0432\u0435\u0440\u043d\u0443\u043b 400 \u0432\u043c\u0435\u0441\u0442\u043e 201, \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0431\u043b\u0438\u0436\u0435: request, response, contract, validation. \u0415\u0441\u043b\u0438 \u043f\u0430\u0434\u0430\u0435\u0442 UI-\u0442\u0435\u0441\u0442, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u043b\u043e\u043c\u0430\u043b\u043e\u0441\u044c: \u0434\u0430\u043d\u043d\u044b\u0435, backend, frontend, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043b\u043e\u043a\u0430\u0442\u043e\u0440, \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0427\u0435\u0442\u0432\u0435\u0440\u0442\u0430\u044f \u2014 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0427\u0435\u0440\u0435\u0437 UI \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0434\u0435\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 API: \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f, \u0434\u043b\u0438\u043d\u0443 \u0441\u0442\u0440\u043e\u043a, \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b \u0438\u043b\u0438 \u0434\u0430\u0442\u044b.<\/p>\n<p>\u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0441\u043c\u0435\u0441\u0442\u0438\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 API.<\/p>\n<h3>\u041a\u0430\u043a \u043c\u044b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c<\/h3>\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u043d\u0430\u0431\u043e\u0440\u0435 API \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043b\u0438\u043c\u0438\u0442\u044b, \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 endpoints \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438. UI \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f.<\/p>\n<p>\u0412 \u0434\u0440\u0443\u0433\u043e\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 API: \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439, \u043f\u0435\u0440\u0438\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0435 \u0444\u043b\u0430\u0433\u0438, \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u0435\u0439\u0441\u044b \u043f\u043e \u0434\u0430\u0442\u0430\u043c, \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 header-\u043e\u0432. UI-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0430\u043c \u0442\u043e\u0436\u0435 \u0435\u0441\u0442\u044c, \u043d\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0430 API, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u043b\u043e\u0435 \u0434\u0435\u0448\u0435\u0432\u043b\u0435 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430.<\/p>\n<p>\u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441\u043c\u0435\u0448\u0430\u043d\u043d\u044b\u0439: API \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f, \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0432\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438. UI-\u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u043c. E2E-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0441\u0442\u0440\u043e\u044f\u0442\u0441\u044f \u0442\u043e\u0447\u0435\u0447\u043d\u043e, \u0447\u0430\u0441\u0442\u043e \u0441 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 API.<\/p>\n<h3>\u041e\u0431\u0449\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 API-\u0441\u043b\u043e\u044f<\/h3>\n<p>\u0412\u043e \u0432\u0441\u0435\u0445 \u0442\u0440\u0435\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u043e\u0445\u043e\u0436\u0430\u044f:<\/p>\n<pre><code>api\/  assertions\/  clients\/  filter\/  models\/  provider\/  requests\/  response\/  steps\/  tests\/<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0432\u0430\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f.<\/p>\n<p>clients \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 HTTP-\u0432\u044b\u0437\u043e\u0432\u044b. requests \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 request body. models \u0438 response \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 DTO. provider \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. steps \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. tests \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u043d\u044b\u043c\u0438.<\/p>\n<p>\u0422\u0435\u0441\u0442 \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u0442\u0438\u043b\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439:<\/p>\n<ol>\n<li>\n<p>\u0421\u043e\u0431\u0440\u0430\u0442\u044c request.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0437\u0432\u0430\u0442\u044c client.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c response.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c step-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 GET\/list \u0438\u043b\u0438 DB helper.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 API-\u043a\u043b\u0438\u0435\u043d\u0442<\/h3>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0432\u044b\u043d\u0435\u0441\u043b\u0438 \u0432 \u043e\u0431\u0449\u0438\u0439 \u0441\u043b\u043e\u0439, \u2014 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 REST Assured, base URL, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 masking filter.<\/p>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u044b\u0439 \u0438 \u043e\u0431\u0435\u0437\u043b\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<pre><code class=\"java\">public class BaseClient {    protected String baseUrl;    protected String accessToken;    protected RequestSpecification baseRequestSpec;    protected ResponseSpecification baseResponseSpec;    public BaseClient() {        baseUrl = ConfigService.get(\"service_url\");        baseRequestSpec = new RequestSpecBuilder()                .setBaseUri(baseUrl)                .setRelaxedHTTPSValidation()                .setContentType(ContentType.JSON)                .addFilter(new MaskingFilter())                .log(LogDetail.ALL)                .build();        baseResponseSpec = new ResponseSpecBuilder()                .log(LogDetail.ALL)                .build();        this.accessToken = given()                .filter(new MaskingFilter())                .relaxedHTTPSValidation()                .formParam(\"username\", ConfigService.get(\"keycloak.username\"))                .formParam(\"password\", ConfigService.get(\"keycloak.password\"))                .formParam(\"client_id\", ConfigService.get(\"client_id\"))                .formParam(\"client_secret\", ConfigService.get(\"client_secret\"))                .formParam(\"grant_type\", ConfigService.get(\"grant_type\"))                .contentType(\"application\/x-www-form-urlencoded\")                .post(ConfigService.get(\"keycloak_url\"))                .then()                .statusCode(200)                .extract()                .body()                .jsonPath()                .getString(\"access_token\");    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u044d\u0442\u043e \u0434\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0442\u0435\u0441\u0442\u044b \u043d\u0435 \u0437\u043d\u0430\u044e\u0442, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d;<\/p>\n<\/li>\n<li>\n<p>base URL \u0438 \u0441\u0435\u043a\u0440\u0435\u0442\u044b \u043d\u0435 \u0440\u0430\u0437\u043c\u0430\u0437\u0430\u043d\u044b \u043f\u043e \u0442\u0435\u0441\u0442\u0430\u043c;<\/p>\n<\/li>\n<li>\n<p>\u0432\u0441\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0435\u0434\u0438\u043d\u044b\u0439 request spec;<\/p>\n<\/li>\n<li>\n<p>\u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0430\u0441\u043a\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u043b\u043e\u0433\u0430\u0445;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u043b\u043e\u0439, \u0430 \u043d\u0435 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 access token. \u042d\u0442\u043e \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0437\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u043f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 API-\u043a\u043b\u0430\u0441\u0441\u043e\u0432.<\/p>\n<pre><code class=\"java\">private static final Object TOKEN_LOCK = new Object();private static volatile String CACHED_ACCESS_TOKEN = null;private String getAccessToken(JasyptConfig config) {    if (CACHED_ACCESS_TOKEN == null) {        synchronized (TOKEN_LOCK) {            if (CACHED_ACCESS_TOKEN == null) {                CACHED_ACCESS_TOKEN = given()                        .filter(new MaskingFilter())                        .relaxedHTTPSValidation()                        .formParam(\"username\", config.get(\"username\"))                        .formParam(\"password\", config.get(\"password\"))                        .formParam(\"client_id\", config.get(\"client_id\"))                        .formParam(\"client_secret\", config.get(\"client_secret\"))                        .formParam(\"grant_type\", config.get(\"grant_type\"))                        .contentType(\"application\/x-www-form-urlencoded\")                        .post(config.get(\"keycloak_url\"))                        .then()                        .statusCode(200)                        .extract()                        .body()                        .jsonPath()                        .getString(\"access_token\");            }        }    }    return CACHED_ACCESS_TOKEN;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439, \u043d\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0435\u043c. \u041e\u043d \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435, \u043d\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u043c\u0443 API-\u043d\u0430\u0431\u043e\u0440\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0435\u0435 \u0438 \u0431\u044b\u0441\u0442\u0440\u0435\u0435.<\/p>\n<h3>\u0415\u0434\u0438\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c response-\u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a<\/h3>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0441\u043b\u043e\u0439 \u2014 \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 REST Assured response.<\/p>\n<pre><code class=\"java\">@RequiredArgsConstructorpublic class AssertableResponse {    private final ValidatableResponse response;    public &lt;T&gt; T as(Class&lt;T&gt; clazz) {        return response.extract().body().as(clazz);    }    public &lt;T&gt; List&lt;T&gt; asList(Class&lt;T&gt; clazz) {        return response.extract().body().jsonPath().getList(\"\", clazz);    }    public AssertableResponse should(Condition condition) {        condition.check(response);        return this;    }    public Response asResponse() {        return response.extract().response();    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u043d\u0430\u0440\u0443\u0436\u0438 \u044d\u0442\u043e \u0434\u0430\u0435\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441:<\/p>\n<pre><code class=\"java\">ToggleResponse response = toggleControllerClient.createToggle(request)        .should(hasStatusCode(HttpStatus.SC_CREATED))        .as(ToggleResponse.class);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u0432 \u044d\u0442\u0438\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 \u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b JSON Schema validation \u043a\u0430\u043a \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430. \u041a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437:<\/p>\n<ul>\n<li>\n<p>\u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u044b;<\/p>\n<\/li>\n<li>\n<p>DTO-\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 request\/response;<\/p>\n<\/li>\n<li>\n<p>\u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432 steps.<\/p>\n<\/li>\n<\/ul>\n<p>Schema validation \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u043d\u043e \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0435 \u043d\u0430 JSON schema, \u0430 \u043d\u0430 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0445 Java-\u043c\u043e\u0434\u0435\u043b\u044f\u0445 \u0438 step-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u0445.<\/p>\n<h3>Providers: \u043a\u0430\u043a \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 API-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/h3>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u0438\u043b\u044c\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 API-\u0441\u043b\u043e\u044f \u2014 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f. \u0412 UI \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <a class=\"mention\" href=\"\/users\/ParameterizedTest\">@ParameterizedTest<\/a>, \u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u0435\u0439\u0441 \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440: \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u043e\u0440\u043c\u0443, \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u044f, \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. \u0415\u0441\u043b\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u0442\u0430\u043a\u043e\u0439 \u043d\u0430\u0431\u043e\u0440 \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e\u0440\u043e\u0433\u0438\u043c \u0438 \u043c\u0435\u043d\u0435\u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u043c.<\/p>\n<p>\u041d\u0430 API-\u0443\u0440\u043e\u0432\u043d\u0435 \u044d\u0442\u043e \u043f\u0440\u043e\u0449\u0435: \u043c\u044b \u0431\u0435\u0440\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 request, \u0442\u043e\u0447\u0435\u0447\u043d\u043e \u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0434\u043d\u043e \u043f\u043e\u043b\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c response. \u0427\u0430\u0441\u0442\u043e \u043d\u043e\u0432\u044b\u0439 \u043a\u0435\u0439\u0441 \u2014 \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u0432 provider.<\/p>\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f title \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f provider \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c\u0438 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:<\/p>\n<pre><code class=\"java\">public class NegativeTestsProviderContent extends ProviderConstants {    public static Stream&lt;Arguments&gt; invalidTitleProvider() {        return Stream.of(                \/\/ \u041f\u0443\u0441\u0442\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430                Arguments.of(\"\", \"newsContents[0].content.title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\"),                \/\/ Null \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435                Arguments.of(null, \"newsContents[0].content.title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\"),                \/\/ \u0421\u0442\u0440\u043e\u043a\u0430 c \u043f\u0440\u043e\u0431\u0435\u043b\u0430\u043c\u0438                Arguments.of(\"   \", \"newsContents[0].content.title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\"),                \/\/ \u0421\u0442\u0440\u043e\u043a\u0430 \u0431\u043e\u043b\u0435\u0435 255 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432                Arguments.of(                        INVALID_TITLE_LENGTH,                        \"newsContents[0].content.title: \u0440\u0430\u0437\u043c\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043e\u0442 0 \u0434\u043e 255\"                )        );    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0430\u043c \u0442\u0435\u0441\u0442 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u043c:<\/p>\n<pre><code class=\"java\">@ParameterizedTest@MethodSource(\"ru.example.news.api.provider.NegativeTestsProviderContent#invalidTitleProvider\")public void creatingNewsWithInvalidTitle(        String invalidTitle,        String expectedError) {    NewsRequest request = CreateNewsRequestBuilder.buildDefaultNewsRequest();    request.getNewsContents().get(0).getContent().setTitle(invalidTitle);    ErrorResponse errorResponse = adminClient.createNews(request)            .should(hasStatusCode(HttpStatus.SC_BAD_REQUEST))            .as(ErrorResponse.class);    Assertions.assertNotNull(errorResponse.getRequestId());    Assertions.assertEquals(expectedError, errorResponse.getErrors().get(0));}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0432\u0430\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>CreateNewsRequestBuilder.buildDefaultNewsRequest() \u0434\u0430\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 request;<\/p>\n<\/li>\n<li>\n<p>\u0442\u0435\u0441\u0442 \u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e title, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0434\u0438\u043d \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443, null, \u0441\u0442\u0440\u043e\u043a\u0443 \u0438\u0437 \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u0438 \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u0438\u043d\u044b;<\/p>\n<\/li>\n<li>\n<p>\u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043a\u0435\u0439\u0441, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 UI-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 API, \u0433\u0434\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u044d\u0442\u0438 \u043a\u0435\u0439\u0441\u044b \u0447\u0435\u0440\u0435\u0437 UI, \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u0440\u043e\u0433\u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b, \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f, \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 DOM \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0442\u0435\u043a\u0441\u0442\u0430 \u043e\u0448\u0438\u0431\u043a\u0438. \u041d\u0430 API-\u0443\u0440\u043e\u0432\u043d\u0435 \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u043e \u0447\u0435\u0442\u044b\u0440\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441 \u043f\u043e\u043d\u044f\u0442\u043d\u043e\u0439 \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u043e\u0439 \u043f\u043e request\/response.<\/p>\n<p>\u042d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043b\u044f:<\/p>\n<ul>\n<li>\n<p>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0445 \u0434\u043b\u0438\u043d \u0441\u0442\u0440\u043e\u043a;<\/p>\n<\/li>\n<li>\n<p>null \u0438 \u043f\u0443\u0441\u0442\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 enum\/status;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u0430\u0432;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0445 \u0434\u0430\u0442;<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 auth\/header scenarios.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0422\u043e\u0442 \u0436\u0435 \u043f\u0440\u0438\u0451\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0438 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445<\/h3>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 typed DTO \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 \u0442\u0438\u043f, \u0442\u0435\u0441\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 raw body \u0447\u0435\u0440\u0435\u0437 Map&lt;String, Object&gt;, \u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0431\u0435\u0440\u0435\u0442 \u0438\u0437 provider-\u0430.<\/p>\n<pre><code class=\"java\">@ParameterizedTest@MethodSource(        \"ru.example.feature.api.provider.ToggleNegativeTestsProvider#invalidEnabledProvider\")public void saveToggleWithInvalidEnabled(        Object invalidEnabled,        List&lt;String&gt; expectedErrors) {    var request = buildToggleBody(            ToggleDataGenerator.generateName(),            ToggleDataGenerator.generateCyrillicDescription(\"\u0422\u0435\u0441\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\"),            invalidEnabled,            null,            null    );    ErrorResponse errorResponse = toggleControllerClient.createToggle(request)            .should(hasStatusCode(HttpStatus.SC_BAD_REQUEST))            .as(ErrorResponse.class);    SaveToggleSteps.assertErrorResponseErrors(errorResponse, expectedErrors);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0430\u0436\u043d\u0443\u044e \u0434\u0435\u0442\u0430\u043b\u044c: API-first \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f happy path. \u041d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, API-\u0441\u043b\u043e\u0439 \u0443\u0434\u043e\u0431\u0435\u043d \u0438\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0438 \u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0442\u043e\u0447\u043d\u043e \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0435 \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<p>\u0412 UI \u0442\u0430\u043a\u043e\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0447\u0430\u0441\u0442\u043e \u0434\u0430\u0436\u0435 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0447\u0435\u0441\u0442\u043d\u043e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u0435 enabled \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435 \u2014 checkbox, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u0441\u0442\u0440\u043e\u043a\u0443. \u041d\u043e backend \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u0442\u0438\u043f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e API \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430.<\/p>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u0441\u0432\u044f\u0437\u043a\u0430 builders + providers + steps \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442\u0441\u044f<\/h3>\n<p>\u0412 \u0441\u0432\u044f\u0437\u043a\u0435 builders + providers + steps \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0443\u0434\u043e\u0431\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f API-\u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>Builder \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u0443\u044e \u0431\u0430\u0437\u0443:<\/p>\n<pre><code class=\"java\">NewsRequest request = CreateNewsRequestBuilder.buildDefaultNewsRequest();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Provider \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0431\u043e\u0440 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"java\">Arguments.of(\"\", \"title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\");Arguments.of(null, \"title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\");Arguments.of(\"   \", \"title: \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Test \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439:<\/p>\n<pre><code class=\"java\">request.getNewsContents().get(0).getContent().setTitle(invalidTitle);ErrorResponse errorResponse = adminClient.createNews(request)        .should(hasStatusCode(HttpStatus.SC_BAD_REQUEST))        .as(ErrorResponse.class);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Steps \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0437\u0430 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438:<\/p>\n<pre><code class=\"java\">SaveToggleSteps.assertErrorResponseErrors(errorResponse, expectedErrors);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u043a\u0435\u0439\u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435 \u043d\u043e\u0432\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c \u043a\u043b\u0430\u0441\u0441\u043e\u043c \u0438 \u043d\u0435 \u043d\u043e\u0432\u044b\u043c UI-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c, \u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 step-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.<\/p>\n<p>\u042d\u0442\u043e \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438. \u041a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043a\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0438, \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f provider. \u041a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 response, \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f DTO \u0438\u043b\u0438 step. \u041a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f endpoint, \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f client. \u0422\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u043c\u0438 \u0438 \u043d\u0435 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 \u043d\u0430\u0431\u043e\u0440 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u0435\u0439.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 API-first \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f: \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0431\u0435\u0437 UI<\/h3>\n<p>\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0435\u0441\u0442\u044c API-\u0442\u0435\u0441\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422\u0435\u0441\u0442 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 response, \u0430 \u0437\u0430\u0442\u0435\u043c \u0443\u0431\u0435\u0436\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 \u043e\u0431\u0449\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0435.<\/p>\n<pre><code class=\"java\">@Test@Tag(\"SMOKE\")public void createTabAndVerifyInList() {    TabRequest request = CreateTabRequestBuilder.buildDefaultTabRequest();    CreateTabResponse createResponse = tabControllerClient.createTab(request)            .should(hasStatusCode(HttpStatus.SC_CREATED))            .as(CreateTabResponse.class);    createdTabIds.add(createResponse.getId());    TabSteps.assertCreateResponseValid(createResponse);    GetTabResponse getTabResponse = getTabFromList(createResponse.getId());    TabSteps.assertTabValid(getTabResponse, createResponse.getId());}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0439 API-first \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>\u043d\u0435 \u043d\u0443\u0436\u0435\u043d \u0431\u0440\u0430\u0443\u0437\u0435\u0440;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u0442\u044c follow-up \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0438\u0441\u043e\u043a;<\/p>\n<\/li>\n<li>\n<p>cleanup \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441;<\/p>\n<\/li>\n<li>\n<p>\u0430\u0441\u0441\u0435\u0440\u0442\u044b \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u044b \u0432 TabSteps;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>Request builder \u043e\u0442\u0434\u0435\u043b\u044f\u0435\u0442 \u0442\u0435\u0441\u0442 \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0441\u0431\u043e\u0440\u043a\u0438 \u0442\u0435\u043b\u0430:<\/p>\n<pre><code class=\"java\">public class CreateTabRequestBuilder {    public static TabRequest buildDefaultTabRequest() {        return TabRequest.builder()                .title(DataGenerator.generateTitle())                .subtitle(DataGenerator.generateSubtitle())                .number(DataGenerator.generateNumber())                .path(DataGenerator.generatePath())                .alfaViewStatus(DataGenerator.getRandomEnum(AlfaViewStatus.class))                .underwritingStatus(DataGenerator.getRandomEnum(UnderwritingStatus.class))                .paymentStatus(DataGenerator.getRandomEnum(PaymentStatus.class))                .contractType(DataGenerator.getRandomEnum(ContractType.class))                .build();    }    public static TabRequest buildRequiredTabRequest() {        return TabRequest.builder()                .title(DataGenerator.generateTitle())                .path(DataGenerator.generatePath())                .build();    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u0441\u0442\u044b \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 JSON \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041e\u043d\u0438 \u0431\u0435\u0440\u0443\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0438 \u043c\u0435\u043d\u044f\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e \u043f\u043e\u043b\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f.<\/p>\n<h3>Steps: \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0436\u0438\u0442\u044c \u0432 \u0442\u0435\u0441\u0442\u0435<\/h3>\n<p>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0432\u0430\u0436\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 API-first \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u2014 steps. \u0412 \u0442\u0435\u0441\u0442\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u044b\u043d\u0438 \u0430\u0441\u0441\u0435\u0440\u0442\u043e\u0432. \u0422\u0435\u0441\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u043e\u043b\u0436\u043d\u0430 \u0436\u0438\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c step-\u043a\u043b\u0430\u0441\u0441\u0435.<\/p>\n<p>\u0425\u043e\u0440\u043e\u0448\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043c\u0435\u043d\u044e \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u0421\u0430\u043c API-\u0442\u0435\u0441\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439:<\/p>\n<pre><code class=\"java\">@Test@Tag(\"SMOKE\")public void getMenuVerifyFields() {    MenuResponse[] menuResponses = menuControllerClient.getMenu()            .should(hasStatusCode(HttpStatus.SC_OK))            .as(MenuResponse[].class);    TabMenuSteps.assertMenuResponseValid(menuResponses);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u0435\u0440\u0435\u0432\u0430 \u043c\u0435\u043d\u044e \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 TabMenuSteps:<\/p>\n<pre><code class=\"java\">@Step(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0438 \u043f\u043e\u043b\u0435\u0439 \u043c\u0435\u043d\u044e\")public static void assertMenuResponseValid(MenuResponse[] menu) {    assertNotNull(menu, \"menu \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c null\");      assertTrue(menu.length &gt; 0, \"menu \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\");        List&lt;MenuResponse&gt; menuList = Arrays.asList(menu);      assertMenuItemsNotEmpty(menuList);      assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043c\u0435\u043d\u044e\",          () -&gt; {              MenuResponse auto = findById(menuList, AUTO_ID);              assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u0410\u0432\u0442\u043e\",                  () -&gt; assertMenuItemEquals(auto, AUTO_ID, AUTO_TITLE),                  () -&gt; assertMenuListNotEmpty(auto.getMenuList(), FIELD_NAME_AUTO),                  () -&gt; assertMenuItemsNotEmpty(auto.getMenuList()),                  () -&gt; assertMenuItemEquals(findById(auto.getMenuList(), BLUECARD_ID), BLUECARD_ID, BLUECARD_TITLE),                  () -&gt; assertMenuItemEquals(findById(auto.getMenuList(), GAP_ID), GAP_ID, GAP_TITLE),                  () -&gt; assertMenuItemEquals(findById(auto.getMenuList(), OSAGO_ID), OSAGO_ID, OSAGO_TITLE),                  () -&gt; assertMenuItemEquals(findById(auto.getMenuList(), CASCO_ID), CASCO_ID, CASCO_TITLE)              );          },            () -&gt; {              MenuResponse health = findById(menuList, HEALTH_ID);              assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u0417\u0434\u043e\u0440\u043e\u0432\u044c\u0435\",                  () -&gt; assertMenuItemEquals(health, HEALTH_ID, HEALTH_TITLE),                  () -&gt; assertMenuListNotEmpty(health.getMenuList(), FIELD_NAME_HEALTH),                  () -&gt; assertMenuItemsNotEmpty(health.getMenuList()),                  () -&gt; {                      MenuResponse medicine = findById(health.getMenuList(), MEDICICNE_ID);                      assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0434\u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u041c\u0435\u0434\u0438\u0446\u0438\u043d\u0430\",                          () -&gt; assertMenuItemEquals(medicine, MEDICICNE_ID, MEDICICNE_TITLE),                          () -&gt; assertMenuListNotEmpty(medicine.getMenuList(), FIELD_NAME_MEDICICNE),                          () -&gt; assertMenuItemsNotEmpty(medicine.getMenuList()),                          () -&gt; assertMenuItemEquals(findById(medicine.getMenuList(), ANTIMATE_ID), ANTIMATE_ID, ANTIMATE_TITLE),                          () -&gt; assertMenuItemEquals(findById(medicine.getMenuList(), NS_ID), NS_ID, NS_TITLE)                      );                  }              );          },          () -&gt; {              MenuResponse journal = findById(menuList, JOURNAL_ID);              assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u041f\u043e\u0438\u0441\u043a\",                  () -&gt; assertMenuItemEquals(journal, JOURNAL_ID, JOURNAL_TITLE),                  () -&gt; assertMenuListNullOrEmpty(journal.getMenuList(), FIELD_NAME_JOURNAL)              );          },          () -&gt; {              MenuResponse widget = findById(menuList, WIDGET_ID);              assertAll(\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u0412\u0438\u0434\u0436\u0435\u0442\u044b\",                  () -&gt; assertMenuItemEquals(widget, WIDGET_ID, WIDGET_TITLE),                  () -&gt; assertMenuListNullOrEmpty(widget.getMenuList(), FIELD_NAME_WIDGET)              );          }      );  }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u0442\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0432\u044b\u0433\u043e\u0434\u043d\u0435\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430 API-\u0443\u0440\u043e\u0432\u043d\u0435?<\/p>\n<p>\u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u043d\u044e:<\/p>\n<ul>\n<li>\n<p>\u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b;<\/p>\n<\/li>\n<li>\n<p>\u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b;<\/p>\n<\/li>\n<li>\n<p>id;<\/p>\n<\/li>\n<li>\n<p>title;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438\u043b\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u0434\u043c\u0435\u043d\u044e;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0436\u0438\u0434\u0430\u0435\u043c\u0430\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 UI, \u0442\u0435\u0441\u0442\u0443 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0431\u044b:<\/p>\n<ul>\n<li>\n<p>\u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043c\u0435\u043d\u044e;<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0441\u043a\u0440\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0440\u043e\u0432\u043d\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0439\u0442\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0432 DOM;<\/p>\n<\/li>\n<li>\n<p>\u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 viewport;<\/p>\n<\/li>\n<li>\n<p>\u0431\u043e\u0440\u043e\u0442\u044c\u0441\u044f \u0441 hover\/click\/animation;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u0442\u043e\u0440\u044b;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>API-\u0442\u0435\u0441\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0447\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u201cbackend \u0432\u0435\u0440\u043d\u0443\u043b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043c\u0435\u043d\u044e?\u201d. UI-\u0442\u0435\u0441\u0442 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0449\u0435: \u201c\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u043c\u0435\u043d\u044e \u0438 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0440\u0430\u0437\u0434\u0435\u043b?\u201d.<\/p>\n<p>\u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438. API \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u0434\u0430\u043d\u043d\u044b\u0435, UI \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435.<\/p>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440 API-first \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f: \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430<\/h3>\n<p>\u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 API-\u0441\u043b\u043e\u0439 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u0435\u043d. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u043b\u0438 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u043e\u0433\u043e header-\u0430. \u0427\u0435\u0440\u0435\u0437 UI \u044d\u0442\u043e \u0431\u044b\u043b \u0431\u044b \u0442\u044f\u0436\u0435\u043b\u044b\u0439 \u0438 \u043d\u0435\u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439. \u0427\u0435\u0440\u0435\u0437 API \u2014 \u0434\u0432\u0430 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b withAuth \u0438 withGatewayUser:<\/p>\n<pre><code class=\"java\">public class ToggleControllerClient extends BaseClient {    private static final String USER_HEADER_VALUE = \"{\\\"roles\\\":[&lt;role-id&gt;]}\";    public AssertableResponse createToggle(Object body) {        return createToggle(body, true, true);    }    public AssertableResponse createToggle(            Object body,            boolean withAuth,            boolean withGatewayUser    ) {        RequestSpecification spec = given()                .spec(baseRequestSpec)                .body(body);        if (withAuth) {            spec.auth().oauth2(accessToken);        }        if (withGatewayUser) {            spec.header(\"x-gateway-user\", USER_HEADER_VALUE);        }        return new AssertableResponse(                spec.post(\"\/feature-api\/toggles\")                        .then()                        .spec(baseResponseSpec)        );    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u043d\u044b\u043c\u0438:<\/p>\n<pre><code class=\"java\">@Testpublic void saveToggleWithoutGatewayHeader() {    ToggleModel request = ToggleRequestBuilder.buildRequiredToggleRequest();    toggleControllerClient.createToggle(request, true, false)            .should(hasStatusCode(HttpStatus.SC_FORBIDDEN));}@Testpublic void saveToggleWithoutAccessToken() {    ToggleModel request = ToggleRequestBuilder.buildRequiredToggleRequest();    toggleControllerClient.createToggle(request, false, true)            .should(hasStatusCode(HttpStatus.SC_UNAUTHORIZED));}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a API-\u0441\u043b\u043e\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0435\u0439 UI-\u043e\u0431\u0432\u044f\u0437\u043a\u0438.<\/p>\n<h3>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 GET\/list<\/h3>\n<p>\u0414\u043b\u044f \u043f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u043c\u044b \u0441\u0442\u0430\u0440\u0430\u043b\u0438\u0441\u044c \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e response-\u043e\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442, \u043f\u043e\u0441\u043b\u0435 POST \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f GET \u0438\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430.<\/p>\n<pre><code class=\"java\">@Testpublic void saveToggleWithPeriodDates() {    ToggleModel request = ToggleRequestBuilder.buildDefaultToggleRequest();    ToggleResponse saveResponse = toggleControllerClient.createToggle(request)            .should(hasStatusCode(HttpStatus.SC_CREATED))            .as(ToggleResponse.class);    SaveToggleSteps.validateRequiredFields(saveResponse);    SaveToggleSteps.assertRequiredFieldsMatchRequest(saveResponse, request);    SaveToggleSteps.assertPeriodMatchesRequest(saveResponse, request);    ToggleResponse toggleFromGet = getToggleFromList(saveResponse.getId());    GetToggleSteps.validateRequiredFields(toggleFromGet);    GetToggleSteps.assertRequiredFieldsMatchSavedResponse(toggleFromGet, saveResponse);    GetToggleSteps.assertPeriodMatchesSavedResponse(toggleFromGet, saveResponse);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0432\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442. API-first \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0432 \u201c\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b\u0438 201 \u0438 \u0443\u0441\u043f\u043e\u043a\u043e\u0438\u043b\u0438\u0441\u044c\u201d. \u0415\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043f\u0438\u0441\u043a\u0435, \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c.<\/p>\n<h3>Raw body \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043c, \u0433\u0434\u0435 DTO \u043c\u0435\u0448\u0430\u0435\u0442 \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0443<\/h3>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0443\u0442\u044c \u2014 typed DTO. \u041d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0435\u0434\u043e\u043c\u043e \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 \u0442\u0438\u043f. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0435 enabled \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c boolean, \u0430 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u0441\u0442\u0440\u043e\u043a\u0443 \u0438\u043b\u0438 \u0447\u0438\u0441\u043b\u043e.<\/p>\n<p>\u0414\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u043c API-\u043a\u043b\u0430\u0441\u0441\u0435 \u0435\u0441\u0442\u044c helper, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 raw body \u0447\u0435\u0440\u0435\u0437 Map.<\/p>\n<pre><code class=\"java\">protected Map&lt;String, Object&gt; buildToggleBody(        String name,        String description,        Object enabled,        String start,        String end) {    Map&lt;String, Object&gt; period = new LinkedHashMap&lt;&gt;();    period.put(\"start\", start);    period.put(\"end\", end);    Map&lt;String, Object&gt; body = new LinkedHashMap&lt;&gt;();    body.put(\"name\", name);    body.put(\"description\", description);    body.put(\"enabled\", enabled);    body.put(\"period\", period);    return body;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441: \u043c\u044b \u043d\u0435 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043e\u0442 \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043d\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0442\u0438\u043f\u043e\u0432.<\/p>\n<h3>API \u043a\u0430\u043a \u0441\u043f\u043e\u0441\u043e\u0431 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u0438\u0442\u044c UI \u043d\u0430 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f\u0445<\/h3>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u2014 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0438 \u0441\u0432\u044f\u0437\u0435\u0439: \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0438\u043f\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0434\u0430\u0442\u044b, \u0441\u0442\u0430\u0442\u0443\u0441\u044b, \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u0430\u0436\u0434\u0443\u044e \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440, UI-\u043d\u0430\u0431\u043e\u0440 \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u044f\u0436\u0435\u043b\u044b\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 API \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u0434\u0432\u0443\u0445 \u0440\u043e\u043b\u044f\u0445:<\/p>\n<ul>\n<li>\n<p>\u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043b\u043e\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0431\u044b\u0441\u0442\u0440\u044b\u0439 setup \u0434\u043b\u044f UI\/E2E.<\/p>\n<\/li>\n<\/ul>\n<p>Builder \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 request-\u0430:<\/p>\n<pre><code class=\"java\">public class CreateNewsRequestBuilder {    public static NewsRequest buildDefaultNewsRequest() {        return NewsRequest.builder()                .newsContents(List.of(buildDefaultNewsContent()))                .build();    }    public static NewsContent buildDefaultNewsContent() {        return NewsContent.builder()                .content(buildDefaultContent())                .attachments(Collections.emptyList())                .channel(\"&lt;channel&gt;\")                .permissions(buildDefaultPermissions())                .build();    }    public static Content buildDefaultContent() {        return Content.builder()                .author(generateFullName(20, 100))                .title(generateRandomText(10, 100))                .previewText(generateRandomText(2, 100))                .fullText(generateFullText(150, 250))                .build();    }    public static Permissions buildDefaultPermissions() {        return Permissions.builder()                .USER_GROUP(List.of(\"&lt;value&gt;\"))                .FILIAL(List.of(\"&lt;value&gt;\"))                .CHANNEL_SALE(List.of(\"&lt;value&gt;\"))                .build();    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>API setup \u0434\u043b\u044f UI-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432<\/h3>\n<p>\u0414\u043b\u044f UI-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0435\u0441\u0442\u044c helper, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 API, \u0430 \u043f\u043e\u0442\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0438\u0445 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.<\/p>\n<pre><code class=\"java\">public String createNewsWithAttachmentsViaApiAndOpenInAdminEditor() {    try {        List&lt;File&gt; files = RandomFileUtil.getRandomFilesFromResources(\"test-files\", 1);        List&lt;Attachments&gt; attachments = new ArrayList&lt;&gt;();        for (File file : files) {            attachments.add(adminClient.uploadFileAndGetAttachment(file));        }        NewsRequest request =                CreateNewsRequestBuilder.buildNewsRequestWithAttachments(attachments);        String title = request.getNewsContents()                .get(0)                .getContent()                .getTitle();        adminClient.createNews(request)                .should(hasStatusCode(201))                .as(CreateNewsAdminResponse.class);        adminNewsListPage.openNewsByTitle(title);        adminEditNewsPage.checkingTheTextNewsEditing();        return title;    } catch (Exception e) {        throw new RuntimeException(\"Failed to create news with attachments via API\", e);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u043f\u0440\u0438\u0435\u043c\u043e\u0432.<\/p>\n<p>\u041c\u044b \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c UI-\u0442\u0435\u0441\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0444\u043e\u0440\u043c\u0443, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b, \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0432\u0435\u0441\u044c \u043f\u0443\u0442\u044c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438. API \u0434\u0435\u043b\u0430\u0435\u0442 setup \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0435\u0435. UI \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u043a \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0443: \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435, \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u043d\u0435 \u0441\u043d\u0438\u0436\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0443\u0436\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0430 API-\u0442\u0435\u0441\u0442\u0430\u043c\u0438. UI-\u0442\u0435\u0441\u0442 \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u0435\u0442 \u0435\u0435, \u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u0432\u043e\u0439 \u0441\u043b\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438.<\/p>\n<h3>UI-\u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f, \u043d\u043e \u0442\u043e\u0447\u0435\u0447\u043d\u043e<\/h3>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 UI-\u0442\u0435\u0441\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441:<\/p>\n<pre><code class=\"java\">@Testpublic void creatingANewsWithRequiredFields() {    adminNewsListPage.clickAddNewsButton();    adminCreateNewsPage.checkingTheTextOfTheNewsContent();    String generatedTitle = DataGenerator.generateRandomText(25, 100);    adminCreateNewsPage.enterTitle(generatedTitle);    adminCreateNewsPage.enterPreviewText(DataGenerator.generateRandomText(10, 100));    adminCreateNewsPage.enterFullText(DataGenerator.generateRandomText(100, 200));    adminCreateNewsPage.selectTheCheckboxUserGroup();    adminCreateNewsPage.selectTheCheckboxFilial();    adminCreateNewsPage.selectTheCheckboxChannelSale();    adminCreateNewsPage.clickOnThePublishNowButton();    adminEditNewsPage.checkingTheTextNewsEditing();    adminEditNewsPage.clickOnTheCloseButton();    adminNewsListPage.checkNewsTitleInList(generatedTitle);    adminNewsListPage.checkNewsStatus(generatedTitle, \"ACTIVE\");}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u043e\u0439 \u0442\u0435\u0441\u0442 \u043d\u0443\u0436\u0435\u043d. \u041e\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u043f\u0443\u0442\u044c:<\/p>\n<ul>\n<li>\n<p>\u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0444\u043e\u0440\u043c\u044b;<\/p>\n<\/li>\n<li>\n<p>\u0432\u0432\u043e\u0434 \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u0432\u044b\u0431\u043e\u0440 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u043e \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c 20 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 title \u0438\u043b\u0438 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u044d\u0442\u043e \u0443\u0436\u0435 \u0437\u043e\u043d\u0430 API. UI \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c, \u0447\u0442\u043e \u0444\u043e\u0440\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0430 \u043d\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0441\u0435\u0445 \u043f\u0440\u0430\u0432\u0438\u043b.<\/p>\n<h3>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044f CI: \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u0438\u043b\u043e \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0421\u0430\u043c\u044b\u0439 \u0437\u0430\u043c\u0435\u0442\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0431\u044b\u043b \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043a\u0440\u0443\u043f\u043d\u044b\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432, \u0433\u0434\u0435 \u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 API-\u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 UI\/E2E. \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u0432 CI \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u043e\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441 16 \u043c\u0438\u043d\u0443\u0442 \u0434\u043e 7\u20138 \u043c\u0438\u043d\u0443\u0442.<\/p>\n<p>\u0427\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>API-\u0442\u0435\u0441\u0442\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u043c;<\/p>\n<\/li>\n<li>\n<p>\u0434\u043b\u044f API \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0432 2 \u043f\u043e\u0442\u043e\u043a\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043a\u043b\u0430\u0441\u0441\u044b API \u0438\u0434\u0443\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0435\u043c\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043b\u043e\u043c\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435;<\/p>\n<\/li>\n<li>\n<p>UI\/E2E \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u043c;<\/p>\n<\/li>\n<li>\n<p>UI-\u043a\u043b\u0430\u0441\u0441\u044b \u0440\u0430\u0441\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0435\u043d\u044b, \u043d\u043e \u043b\u043e\u0433\u0438\u043d \u0437\u0430\u0449\u0438\u0449\u0435\u043d lock-\u043e\u043c;<\/p>\n<\/li>\n<li>\n<p>cleanup \u0432\u044b\u043d\u0435\u0441\u0435\u043d \u0432 JUnit extensions \u0438 DB helpers.<\/p>\n<\/li>\n<\/ul>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 parallel-api \u043f\u0440\u043e\u0444\u0438\u043b\u044f:<\/p>\n<pre><code class=\"xml\">&lt;profile&gt;    &lt;id&gt;parallel-api&lt;\/id&gt;    &lt;properties&gt;        &lt;tag&gt;API&lt;\/tag&gt;    &lt;\/properties&gt;    &lt;build&gt;        &lt;plugins&gt;            &lt;plugin&gt;                &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;                &lt;artifactId&gt;maven-surefire-plugin&lt;\/artifactId&gt;                &lt;configuration&gt;                    &lt;groups&gt;${tag}&lt;\/groups&gt;                    &lt;forkCount&gt;1&lt;\/forkCount&gt;                    &lt;reuseForks&gt;true&lt;\/reuseForks&gt;                    &lt;systemPropertyVariables&gt;                        &lt;junit.jupiter.execution.parallel.enabled&gt;true&lt;\/junit.jupiter.execution.parallel.enabled&gt;                        &lt;junit.jupiter.execution.parallel.mode.default&gt;concurrent&lt;\/junit.jupiter.execution.parallel.mode.default&gt;                        &lt;junit.jupiter.execution.parallel.mode.classes.default&gt;same_thread&lt;\/junit.jupiter.execution.parallel.mode.classes.default&gt;                        &lt;junit.jupiter.execution.parallel.config.strategy&gt;fixed&lt;\/junit.jupiter.execution.parallel.config.strategy&gt;                        &lt;junit.jupiter.execution.parallel.config.fixed.parallelism&gt;2&lt;\/junit.jupiter.execution.parallel.config.fixed.parallelism&gt;                        &lt;junit.jupiter.testclass.order.default&gt;                            ru.example.tests.junit.ExecutionOrderClassOrderer                        &lt;\/junit.jupiter.testclass.order.default&gt;                    &lt;\/systemPropertyVariables&gt;                &lt;\/configuration&gt;            &lt;\/plugin&gt;        &lt;\/plugins&gt;    &lt;\/build&gt;&lt;\/profile&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u043a\u043b\u0430\u0441\u0441\u044b \u0438\u0434\u0443\u0442 same_thread, \u0430 \u043c\u0435\u0442\u043e\u0434\u044b concurrent? \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0435\u0441\u0442\u044c \u043e\u0431\u0449\u0438\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, cleanup \u0438 \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043a\u043b\u0430\u0441\u0441\u043e\u0432. \u041f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0445\u0430\u043e\u0442\u0438\u0447\u043d\u0430\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u043b\u0430 \u0431\u044b \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441: \u0443\u0441\u043a\u043e\u0440\u044f\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0435\u043c\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b.<\/p>\n<p>\u0414\u043b\u044f UI-\u043f\u0440\u043e\u0444\u0438\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u0440\u0443\u0433\u0430\u044f: \u043a\u043b\u0430\u0441\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u0434\u0442\u0438 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e, \u043d\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u0430 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u043c lock-\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u043d\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432\u0430\u043b\u0438 \u0432 \u043e\u0431\u0449\u0435\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <a class=\"mention\" href=\"\/users\/TestInstance\">@TestInstance<\/a>(TestInstance.Lifecycle.PER_CLASS), \u0447\u0442\u043e\u0431\u044b \u043e\u0434\u0438\u043d \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u0430 \u0442\u043e\u043a\u0435\u043d \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0441\u044f \u0437\u0430\u043d\u043e\u0432\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<h3>\u0427\u0442\u043e \u0435\u0449\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u043b\u043e \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c<\/h3>\n<p>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u044c \u0438\u0441\u0442\u043e\u0440\u0438\u0438. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0442\u0430\u043c, \u0433\u0434\u0435 \u044d\u0442\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.<\/p>\n<p>\u0427\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u043b\u043e:<\/p>\n<ul>\n<li>\n<p>API-\u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0441\u043a\u0440\u044b\u043b\u0438 HTTP-\u0434\u0435\u0442\u0430\u043b\u0438 \u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0434\u0435\u0448\u0435\u0432\u043b\u0435.<\/p>\n<\/li>\n<li>\n<p>Builders \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 request body.<\/p>\n<\/li>\n<li>\n<p>Providers \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 API-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0434\u0443\u0432\u0430\u043d\u0438\u044f UI-\u043d\u0430\u0431\u043e\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p>Steps \u0432\u044b\u043d\u0435\u0441\u043b\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c\u044b\u0435 \u0430\u0441\u0441\u0435\u0440\u0442\u044b \u0438\u0437 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>Follow-up GET\/list \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u0435\u0437 UI.<\/p>\n<\/li>\n<li>\n<p>API setup \u0434\u043b\u044f UI \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<\/li>\n<li>\n<p>DB cleanup \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430.<\/p>\n<\/li>\n<li>\n<p>Token cache \u0432 \u0431\u043e\u043b\u044c\u0448\u043e\u043c API-\u043d\u0430\u0431\u043e\u0440\u0435 \u0441\u043d\u0438\u0437\u0438\u043b \u043b\u0438\u0448\u043d\u0438\u0435 auth-\u0432\u044b\u0437\u043e\u0432\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 API \u0438 UI \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435: \u043c\u044b \u043d\u0435 \u0443\u0434\u0430\u043b\u0438\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. \u041c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u0438\u0445 \u0442\u0443\u0434\u0430, \u0433\u0434\u0435 \u043e\u043d\u0438 \u0434\u0435\u0448\u0435\u0432\u043b\u0435 \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0435\u0435.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u201c\u043d\u0435\u043b\u044c\u0437\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0431\u0435\u0437 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f\u201d \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c API-\u0442\u0435\u0441\u0442\u043e\u043c. UI \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0434\u0438\u043d \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439: \u0444\u043e\u0440\u043c\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e. \u042d\u0442\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0440\u0438\u0441\u043a\u0438, \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438\u0445 \u043b\u0443\u0447\u0448\u0435 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u043b\u043e\u044f\u0445.<\/p>\n<p>API-first \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e HTTP \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. \u041e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0435\u0448\u0435\u0432\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438: builder \u0434\u0430\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u0443\u044e \u0431\u0430\u0437\u0443, provider \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442 \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, client \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 HTTP, \u0430 steps \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 \u0430\u0441\u0441\u0435\u0440\u0442\u044b \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/p>\n<h3>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h3>\n<p>API-first \u043d\u0435 \u0440\u0435\u0448\u0430\u0435\u0442 \u0432\u0441\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, API-\u0442\u0435\u0441\u0442 \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u0435\u043d UI.<\/p>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, API-\u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432, DTO \u0438 builders. \u0415\u0441\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u0442\u043e\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c.<\/p>\n<p>\u0412-\u0442\u0440\u0435\u0442\u044c\u0438\u0445, \u043d\u0443\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f \u043a API \u0438 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0434\u043e\u043c\u0435\u043d\u0430. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u041d\u0443\u0436\u043d\u044b \u0431\u0438\u0437\u043d\u0435\u0441-\u0430\u0441\u0441\u0435\u0440\u0442\u044b.<\/p>\n<p>\u0412-\u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044b\u0445, DB helpers \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e\u0441\u0442\u0438. Cleanup \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u0438\u043d\u0430\u0447\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u0440\u0435\u0434\u0438\u0442\u044c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0412-\u043f\u044f\u0442\u044b\u0445, E2E \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043d\u0443\u0436\u043d\u044b. \u041d\u043e \u0438\u0445 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e: \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0435 \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b, \u0433\u0434\u0435 \u0432\u0430\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0432\u044f\u0437\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<h3>\u0412\u044b\u0432\u043e\u0434<\/h3>\n<p>API-first \u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u0445 \u2014 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e \u043e\u0442\u043a\u0430\u0437 \u043e\u0442 UI. \u042d\u0442\u043e \u043f\u0440\u043e \u0437\u0440\u0435\u043b\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a.<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e 80\/15\/5 \u043f\u043e\u043c\u043e\u0433\u043b\u043e \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u0438\u0442\u044c UI-\u0441\u043b\u043e\u0439:<\/p>\n<ul>\n<li>\n<p>API \u0437\u0430\u0431\u0440\u0430\u043b \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u043f\u0440\u0430\u0432\u0430 \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>UI \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>E2E \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u0434\u043b\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0445 \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u044d\u0442\u043e \u0434\u0430\u043b\u043e \u0431\u043e\u043b\u0435\u0435 \u0431\u044b\u0441\u0442\u0440\u0443\u044e \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c: \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043a\u0440\u0443\u043f\u043d\u044b\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u0432 CI \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u043e\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441 16 \u043c\u0438\u043d\u0443\u0442 \u0434\u043e 7\u20138 \u043c\u0438\u043d\u0443\u0442 \u0437\u0430 \u0441\u0447\u0435\u0442 API-first \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 \u0437\u0430\u043f\u0443\u0441\u043a\u0430.<\/p>\n<p>\u0421\u0430\u043c\u043e\u0435 \u0432\u0430\u0436\u043d\u043e\u0435 \u2014 \u043d\u0435 \u0441\u0430\u043c\u043e \u0447\u0438\u0441\u043b\u043e \u043c\u0438\u043d\u0443\u0442, \u0430 \u043f\u0440\u0438\u043d\u0446\u0438\u043f. \u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441: \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u0441\u043b\u043e\u0435 \u0434\u0435\u0448\u0435\u0432\u043b\u0435, \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435 \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0435\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0440\u0438\u0441\u043a?<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0440\u0438\u0441\u043a \u0432 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0435 \u2014 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e API. \u0415\u0441\u043b\u0438 \u0440\u0438\u0441\u043a \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u2014 UI. \u0415\u0441\u043b\u0438 \u0440\u0438\u0441\u043a \u0432 \u0441\u043a\u0432\u043e\u0437\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u043a\u0435 \u0441\u0438\u0441\u0442\u0435\u043c \u2014 E2E.<\/p>\n<p>\u0418\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u043e\u0439 \u0431\u0430\u043b\u0430\u043d\u0441 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0435 \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043d\u0435 \u0442\u0435\u0440\u044f\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c \u0431\u044b\u0441\u0442\u0440\u0435\u0435.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1046313\/\">https:\/\/habr.com\/ru\/articles\/1046313\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0414\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440\u042d\u0442\u043e \u043d\u0435 \u0441\u0435\u0440\u0435\u0431\u0440\u044f\u043d\u0430\u044f \u043f\u0443\u043b\u044f \u0438 \u043d\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u0433\u043c\u0430.API-first \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b. \u041e\u043d \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0431\u044b\u043b\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 API, \u0430 UI-\u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0447\u0430\u043b\u0438 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0448\u0435\u0432\u043b\u0435 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0438\u0436\u0435.\u041c\u044b \u043d\u0435 \u043f\u0440\u0438\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 UI-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. UI \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d. \u0412\u043e\u043f\u0440\u043e\u0441 \u043d\u0435 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b API-\u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438, \u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c:API \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e, \u043f\u0440\u0430\u0432\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438;UI \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435;E2E \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0445 \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432.\u0427\u0442\u043e \u0432\u0430\u0441 \u0436\u0434\u0435\u0442 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435\u0410\u043d\u0430\u043b\u0438\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c UI-heavy \u043f\u043e\u0434\u0445\u043e\u0434\u0430.\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API-first \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0430 Java, REST Assured \u0438 JUnit 5.\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432.\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b: API clients, builders, response wrappers, steps, providers, base-\u043a\u043b\u0430\u0441\u0441\u044b.\u041a\u0430\u043a API \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0433\u0440\u0443\u0436\u0430\u0442\u044c UI \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f.\u041a\u0430\u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f API-\u0442\u0435\u0441\u0442\u043e\u0432 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0440\u0430\u0449\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.\u041a\u0430\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044f API \u0438 UI-\u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u0430 \u0432\u0440\u0435\u043c\u044f CI.\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0438 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u044b.\u041a\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d QA Automation \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0440\u043e\u044f\u0442 \u0438\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 Java-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432.\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0433\u0434\u0430:UI-\u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e;\u043f\u0440\u043e\u0433\u043e\u043d \u0432 CI \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043e\u043b\u0433\u043e;\u043f\u0430\u0434\u0435\u043d\u0438\u0435 UI-\u0442\u0435\u0441\u0442\u0430 \u0441\u043b\u043e\u0436\u043d\u043e \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c;\u0431\u0438\u0437\u043d\u0435\u0441-\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0434\u043b\u0438\u043d\u043d\u044b\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438;\u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043b\u043e\u0436\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441;\u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0445\u043e\u0447\u0435\u0442 \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c, \u043d\u043e \u043d\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e.\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435: \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 API-first \u043f\u043e\u0434\u0445\u043e\u0434 \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438API-first \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043c\u0430\u0441\u0441\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0430 \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e \u201c\u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0447\u0435\u0440\u0435\u0437 API\u201d. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0433\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435:80% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 API-\u0443\u0440\u043e\u0432\u0435\u043d\u044c: \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u043f\u0440\u0430\u0432\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b;15% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 UI-\u0443\u0440\u043e\u0432\u0435\u043d\u044c: \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u0444\u043e\u0440\u043c\u044b, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435;5% \u0442\u0435\u0441\u0442\u043e\u0432 \u2014 E2E: \u0441\u043a\u0432\u043e\u0437\u043d\u044b\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b.\u042d\u0442\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u043a \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043a\u043e\u043d. \u0414\u043b\u044f \u043d\u0430\u0441 \u043e\u043d\u043e \u0441\u0442\u0430\u043b\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u043c: \u0435\u0441\u043b\u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0434\u0435\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 API-\u0443\u0440\u043e\u0432\u043d\u0435, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0442\u0430\u0449\u0438\u0442\u044c \u0435\u0433\u043e \u0432 UI \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0442\u0430\u043a \u201c\u0432\u0438\u0434\u043d\u0435\u0435\u201d.\u0411\u043e\u043b\u044c UI-heavy \u043f\u043e\u0434\u0445\u043e\u0434\u0430\u041a\u043e\u0433\u0434\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e, UI-\u0442\u0435\u0441\u0442\u044b \u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0441\u0430\u043c\u044b\u043c \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c: \u043e\u043d\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u043e\u043c. \u041d\u043e \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.\u041f\u0435\u0440\u0432\u0430\u044f \u2014 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c. UI-\u0442\u0435\u0441\u0442\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u043d\u0430\u0439\u0442\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0438 \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0440\u0435\u0430\u043a\u0446\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430. \u0415\u0441\u043b\u0438 \u0442\u0430\u043a\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u043c\u043d\u043e\u0433\u043e, CI \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c.\u0412\u0442\u043e\u0440\u0430\u044f \u2014 \u0445\u0440\u0443\u043f\u043a\u043e\u0441\u0442\u044c. UI-\u0442\u0435\u0441\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438, \u043d\u043e \u0438 \u043e\u0442 \u043b\u043e\u043a\u0430\u0442\u043e\u0440\u043e\u0432, \u0432\u0435\u0440\u0441\u0442\u043a\u0438, \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a, \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u043e\u0448\u0438\u0431\u043e\u043a.\u0422\u0440\u0435\u0442\u044c\u044f \u2014 \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0430. \u0415\u0441\u043b\u0438 API-\u0442\u0435\u0441\u0442 \u0432\u0435\u0440\u043d\u0443\u043b 400 \u0432\u043c\u0435\u0441\u0442\u043e 201, \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0431\u043b\u0438\u0436\u0435: request, response, contract, validation. \u0415\u0441\u043b\u0438 \u043f\u0430\u0434\u0430\u0435\u0442 UI-\u0442\u0435\u0441\u0442, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u043b\u043e\u043c\u0430\u043b\u043e\u0441\u044c: \u0434\u0430\u043d\u043d\u044b\u0435, backend, frontend, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043b\u043e\u043a\u0430\u0442\u043e\u0440, \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0438\u043b\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435.\u0427\u0435\u0442\u0432\u0435\u0440\u0442\u0430\u044f \u2014 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0427\u0435\u0440\u0435\u0437 UI \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u0443\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0434\u0435\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 API: \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f, \u0434\u043b\u0438\u043d\u0443 \u0441\u0442\u0440\u043e\u043a, \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0441\u0442\u0430\u0442\u0443\u0441\u044b \u0438\u043b\u0438 \u0434\u0430\u0442\u044b.\u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0441\u043c\u0435\u0441\u0442\u0438\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 API.\u041a\u0430\u043a \u043c\u044b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u0412 \u043e\u0434\u043d\u043e\u043c \u043d\u0430\u0431\u043e\u0440\u0435 API \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043b\u0438\u043c\u0438\u0442\u044b, \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 endpoints \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438. UI \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f.\u0412 \u0434\u0440\u0443\u0433\u043e\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 API: \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439, \u043f\u0435\u0440\u0438\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u044b\u0435 \u0444\u043b\u0430\u0433\u0438, \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u0435\u0439\u0441\u044b \u043f\u043e \u0434\u0430\u0442\u0430\u043c, \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 header-\u043e\u0432. UI-\u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0442\u0430\u043c \u0442\u043e\u0436\u0435 \u0435\u0441\u0442\u044c, \u043d\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0430 API, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u043b\u043e\u0435 \u0434\u0435\u0448\u0435\u0432\u043b\u0435 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u0435\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430.\u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441\u043c\u0435\u0448\u0430\u043d\u043d\u044b\u0439: API \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f, \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0432\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438. UI-\u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u043c. E2E-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0441\u0442\u0440\u043e\u044f\u0442\u0441\u044f \u0442\u043e\u0447\u0435\u0447\u043d\u043e, \u0447\u0430\u0441\u0442\u043e \u0441 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 API.\u041e\u0431\u0449\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 API-\u0441\u043b\u043e\u044f\u0412\u043e \u0432\u0441\u0435\u0445 \u0442\u0440\u0435\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u043e\u0445\u043e\u0436\u0430\u044f:api\/  assertions\/  clients\/  filter\/  models\/  provider\/  requests\/  response\/  steps\/  tests\/\u042d\u0442\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0432\u0430\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f.clients \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 HTTP-\u0432\u044b\u0437\u043e\u0432\u044b. requests \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 request body. models \u0438 response \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 DTO. provider \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. steps \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. tests \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u043d\u044b\u043c\u0438.\u0422\u0435\u0441\u0442 \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u0442\u0438\u043b\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439:\u0421\u043e\u0431\u0440\u0430\u0442\u044c request.\u0412\u044b\u0437\u0432\u0430\u0442\u044c client.\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441.\u0414\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c response.\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c step-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.\u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 GET\/list \u0438\u043b\u0438 DB helper.\u0411\u0430\u0437\u043e\u0432\u044b\u0439 API-\u043a\u043b\u0438\u0435\u043d\u0442\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0432\u044b\u043d\u0435\u0441\u043b\u0438 \u0432 \u043e\u0431\u0449\u0438\u0439 \u0441\u043b\u043e\u0439, \u2014 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 REST Assured, base URL, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 masking filter.\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u044b\u0439 \u0438 \u043e\u0431\u0435\u0437\u043b\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430:public class BaseClient {    protected String baseUrl;    protected String accessToken;    protected RequestSpecification baseRequestSpec;    protected ResponseSpecification baseResponseSpec;    public BaseClient() {        baseUrl = ConfigService.get(&#171;service_url&#187;);        baseRequestSpec = new RequestSpecBuilder()                .setBaseUri(baseUrl)                .setRelaxedHTTPSValidation()                .setContentType(ContentType.JSON)                .addFilter(new MaskingFilter())                .log(LogDetail.ALL)                .build();        baseResponseSpec = new ResponseSpecBuilder()                .log(LogDetail.ALL)                .build();        this.accessToken = given()                .filter(new MaskingFilter())                .relaxedHTTPSValidation()                .formParam(&#171;username&#187;, ConfigService.get(&#171;keycloak.username&#187;))                .formParam(&#171;password&#187;, ConfigService.get(&#171;keycloak.password&#187;))                .formParam(&#171;client_id&#187;, ConfigService.get(&#171;client_id&#187;))                .formParam(&#171;client_secret&#187;, ConfigService.get(&#171;client_secret&#187;))                .formParam(&#171;grant_type&#187;, ConfigService.get(&#171;grant_type&#187;))                .contentType(&#171;application\/x-www-form-urlencoded&#187;)                .post(ConfigService.get(&#171;keycloak_url&#187;))                .then()                .statusCode(200)                .extract()                .body()                .jsonPath()                .getString(&#171;access_token&#187;);    }}\u0427\u0442\u043e \u044d\u0442\u043e \u0434\u0430\u0435\u0442:\u0442\u0435\u0441\u0442\u044b \u043d\u0435 \u0437\u043d\u0430\u044e\u0442, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d;base URL \u0438 \u0441\u0435\u043a\u0440\u0435\u0442\u044b \u043d\u0435 \u0440\u0430\u0437\u043c\u0430\u0437\u0430\u043d\u044b \u043f\u043e \u0442\u0435\u0441\u0442\u0430\u043c;\u0432\u0441\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0435\u0434\u0438\u043d\u044b\u0439 request spec;\u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0430\u0441\u043a\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u043b\u043e\u0433\u0430\u0445;\u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u043b\u043e\u0439, \u0430 \u043d\u0435 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.\u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 access token. \u042d\u0442\u043e \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0437\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u043f\u0440\u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 API-\u043a\u043b\u0430\u0441\u0441\u043e\u0432.private static final Object TOKEN_LOCK = new Object();private static volatile String CACHED_ACCESS_TOKEN = null;private String getAccessToken(JasyptConfig config) {    if (CACHED_ACCESS_TOKEN == null) {        synchronized (TOKEN_LOCK) {            if (CACHED_ACCESS_TOKEN == null) {                CACHED_ACCESS_TOKEN = given()                        .filter(new MaskingFilter())                        .relaxedHTTPSValidation()                        .formParam(&#171;username&#187;, config.get(&#171;username&#187;))                        .formParam(&#171;password&#187;, config.get(&#171;password&#187;))                        .formParam(&#171;client_id&#187;, config.get(&#171;client_id&#187;))                        .formParam(&#171;client_secret&#187;, config.get(&#171;client_secret&#187;))                        .formParam(&#171;grant_type&#187;, config.get(&#171;grant_type&#187;))                        .contentType(&#171;application\/x-www-form-urlencoded&#187;)                        .post(config.get(&#171;keycloak_url&#187;))                        .then()                        .statusCode(200)                        .extract()                        .body()                        .jsonPath()                        .getString(&#171;access_token&#187;);            }        }    }    return CACHED_ACCESS_TOKEN;}\u042d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439, \u043d\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043f\u0440\u0438\u0435\u043c. \u041e\u043d \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0441\u0430\u043c \u043f\u043e \u0441\u0435\u0431\u0435, \u043d\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u043c\u0443 API-\u043d\u0430\u0431\u043e\u0440\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0435\u0435 \u0438 \u0431\u044b\u0441\u0442\u0440\u0435\u0435.\u0415\u0434\u0438\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c response-\u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0441\u043b\u043e\u0439 \u2014 \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 REST Assured response.@RequiredArgsConstructorpublic class AssertableResponse {    private final ValidatableResponse response;    public &lt;T&gt; T as(Class&lt;T&gt; clazz) {        return response.extract().body().as(clazz);    }    public &lt;T&gt; List&lt;T&gt; asList(Class&lt;T&gt; clazz) {        return response.extract().body().jsonPath().getList(&#171;&#187;, clazz);    }    public AssertableResponse should(Condition condition) {        condition.check(response);        return this;    }    public Response asResponse() {        return response.extract().response();    }}\u0421\u043d\u0430\u0440\u0443\u0436\u0438 \u044d\u0442\u043e \u0434\u0430\u0435\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441:ToggleResponse response = toggleControllerClient.createToggle(request)        .should(hasStatusCode(HttpStatus.SC_CREATED))        .as(ToggleResponse.class);\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u0432 \u044d\u0442\u0438\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 \u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b JSON Schema validation \u043a\u0430\u043a \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430. \u041a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437:\u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u044b;DTO-\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e;\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f;\u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 request\/response;\u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432 steps.Schema validation \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u043d\u043e \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0435 \u043d\u0430 JSON schema, \u0430 \u043d\u0430 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0445 Java-\u043c\u043e\u0434\u0435\u043b\u044f\u0445 \u0438 step-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u0445.Providers: \u043a\u0430\u043a \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 API-\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u0438\u043b\u044c\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 API-\u0441\u043b\u043e\u044f \u2014 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f. \u0412 UI \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c @ParameterizedTest, \u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u0435\u0439\u0441 \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440: \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u043e\u0440\u043c\u0443, \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u044f, \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. \u0415\u0441\u043b\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043c\u043d\u043e\u0433\u043e, \u0442\u0430\u043a\u043e\u0439 \u043d\u0430\u0431\u043e\u0440 \u0431\u044b\u0441\u0442\u0440\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e\u0440\u043e\u0433\u0438\u043c \u0438 \u043c\u0435\u043d\u0435\u0435&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-483251","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/483251","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=483251"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/483251\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=483251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=483251"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=483251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}