{"id":454201,"date":"2025-04-01T17:03:28","date_gmt":"2025-04-01T17:03:28","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=454201"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=454201","title":{"rendered":"<span>API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b \u043d\u0430 Python \u0441 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u043d\u0430 CI\/CD \u0438 Allure \u043e\u0442\u0447\u0435\u0442\u043e\u043c<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Python, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 <strong>best practices.<\/strong> \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0438\u0445 \u0437\u0430\u043f\u0443\u0441\u043a \u0432 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/CI\/CD\" rel=\"noopener noreferrer nofollow\"><strong>CI\/CD<\/strong><\/a> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Actions<\/strong><\/a> \u0438 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/9\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u0441 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432. \u0426\u0435\u043b\u044c \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b, \u043d\u043e \u0438 \u043d\u0430\u0443\u0447\u0438\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0438\u0445 \u0432 CI\/CD, \u043f\u043e\u043b\u0443\u0447\u0430\u044f \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u043e\u0442\u0447\u0451\u0442\u044b \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445.<\/p>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/features\/actions\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Actions<\/strong><\/a>, \u043d\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0438 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 CI\/CD-\u0441\u0438\u0441\u0442\u0435\u043c, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <a href=\"https:\/\/docs.gitlab.com\/ci\/\" rel=\"noopener noreferrer nofollow\">GitLab CI<\/a>, <a href=\"https:\/\/circleci.com\/\" rel=\"noopener noreferrer nofollow\">CircleCI<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/www.jenkins.io\/\" rel=\"noopener noreferrer nofollow\">Jenkins<\/a> \u2014 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441. \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 Allure-\u043e\u0442\u0447\u0451\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u043d\u0430 <a href=\"https:\/\/pages.github.com\/\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Pages<\/strong><\/a>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432.<\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 API \u2014 <a href=\"https:\/\/en.wikipedia.org\/wiki\/REST\" rel=\"noopener noreferrer nofollow\"><strong>REST API<\/strong><\/a>, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c <a href=\"https:\/\/sampleapis.com\/api-list\/fakebank\" rel=\"noopener noreferrer nofollow\">FakeBank<\/a>. \u042d\u0442\u043e \u0443\u0447\u0435\u0431\u043d\u043e\u0435 API, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0435\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u043c\u0438 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438. \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/api.sampleapis.com\/fakebank\/accounts<\/strong><\/a>.<\/p>\n<h2>\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h2>\n<p>\u0412\u043e\u0442 \u0441\u0442\u0435\u043a \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/www.python.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Python 3.12<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>Pytest<\/strong><\/a> \u2014 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a REST API<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/pydantic_settings\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic Settings<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/faker.readthedocs.io\/en\/master\/\" rel=\"noopener noreferrer nofollow\"><strong>Faker<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/allurereport.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Allure<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u0447\u0451\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>jsonschema<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c\u044b JSON-\u043e\u0442\u0432\u0435\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/pytest-dev\/pytest-xdist\" rel=\"noopener noreferrer nofollow\"><strong>pytest-xdist<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 HTTPX, \u0430 \u043d\u0435 Requests?<\/h4>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/requests.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Requests<\/strong><\/a> \u0445\u043e\u0442\u044c \u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0430, \u043d\u043e \u0443\u0436\u0435 \u0434\u0430\u0432\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c\u0441\u044f. \u0423 \u043d\u0435\u0451 \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 <a href=\"https:\/\/github.com\/psf\/requests\/issues\/3855#issuecomment-277931774\" rel=\"noopener noreferrer nofollow\"><strong>\u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0442\u0438\u043f\u043e\u0432<\/strong><\/a>, \u0445\u043e\u0442\u044f \u044d\u0442\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 Python \u0435\u0449\u0451 \u0432 \u0432\u0435\u0440\u0441\u0438\u0438 3.5 (\u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0441\u043a\u043e\u0440\u043e \u0432\u044b\u0439\u0434\u0435\u0442 \u0443\u0436\u0435 3.14). \u041f\u0440\u043e\u0448\u043b\u043e <strong>\u0431\u043e\u043b\u0435\u0435 10 \u043b\u0435\u0442<\/strong>, \u043d\u043e \u0432 Requests \u0442\u0430\u043a \u0438 \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432, \u0447\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0441\u043b\u0430\u0431\u043e\u0439 \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<p><a href=\"https:\/\/requests.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Requests<\/strong><\/a> \u0432 \u0441\u0432\u043e\u0451 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0432\u043e\u0435\u0432\u0430\u043b\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f <strong>\u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0435<\/strong> \u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044e \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u0445 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432. \u041d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0435\u0441\u0442\u044c <strong>\u0431\u043e\u043b\u0435\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435, \u043c\u043e\u0449\u043d\u043e\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/strong> \u2014 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a>.<\/p>\n<p>\u0427\u0442\u043e \u0434\u0430\u0451\u0442 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 Requests:<\/p>\n<ul>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0442\u0438\u043f\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 <a href=\"https:\/\/www.python-httpx.org\/advanced\/clients\/\" rel=\"noopener noreferrer nofollow\"><strong>Client<\/strong><\/a> \u0434\u043b\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.python-httpx.org\/advanced\/event-hooks\/\" rel=\"noopener noreferrer nofollow\"><strong>Event hooks<\/strong><\/a> (\u0445\u0443\u043a\u0438 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439)<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 <strong>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445<\/strong> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 <strong>HTTP\/2<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0441\u0451 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> <strong>\u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c<\/strong>. Requests, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0442\u0430\u043a \u0438 \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0431\u0435\u0437 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u043f\u0438\u0448\u0435\u0442\u0435 <strong>legacy-\u043a\u043e\u0434<\/strong> \u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 <strong>\u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Python<\/strong>, \u0442\u043e <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a><strong> \u2014 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440<\/strong>.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 Pydantic?<\/h4>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e: <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a><strong> \u2014 \u044d\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0434\u0435-\u0444\u0430\u043a\u0442\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 Python<\/strong>.<\/p>\n<p>\u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0412\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e <strong>\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c<\/strong> JSON<\/p>\n<\/li>\n<li>\n<p>\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u0433\u0443\u044e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 <a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/pydantic_settings\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic Settings<\/strong><\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0423 <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u043f\u043e\u0447\u0442\u0438 \u043d\u0435\u0442 \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u0445 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432. <strong>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 <\/strong><a href=\"https:\/\/docs.python.org\/3\/library\/dataclasses.html\" rel=\"noopener noreferrer nofollow\"><strong>dataclasses<\/strong><\/a> \u0432 Python \u0434\u0430\u0436\u0435 \u0431\u043b\u0438\u0437\u043a\u043e \u043d\u0435 \u0434\u0430\u044e\u0442 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u044b <strong>\u0447\u0438\u0441\u0442\u044b\u0435, \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u0435 \u0438 \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435<\/strong> \u2014 <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 pytest?<\/h4>\n<p>\u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e <a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a><strong> \u2014 \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0438\u0439 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f Python<\/strong>.<\/p>\n<ul>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c<\/strong>: \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u044b\u0435, \u0442\u0430\u043a \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b<\/p>\n<\/li>\n<li>\n<p><strong>\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c<\/strong>: \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b, \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0438, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Allure<\/p>\n<\/li>\n<li>\n<p><strong>\u0427\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c<\/strong>: \u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0447\u0438\u0441\u0442\u044b\u043c\u0438 \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c<\/strong>: pytest \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 Python<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430 \u0444\u043e\u043d\u0435 \u044d\u0442\u043e\u0433\u043e <a href=\"https:\/\/behave.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Behave<\/strong><\/a><strong>, <\/strong><a href=\"https:\/\/robotframework.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Robot Framework<\/strong><\/a> \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u044b\u043c\u0438 \u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u044e\u0449\u0438\u043c\u0438 \u0436\u0438\u0437\u043d\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<ul>\n<li>\n<p><strong>\u041b\u0438\u0448\u043d\u044f\u044f \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f<\/strong> \u2014 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043b\u0430\u0434\u043a\u0438<\/strong> \u2014 \u0442\u0435\u0441\u0442\u044b \u0432 \u0441\u0442\u0438\u043b\u0435 <a href=\"https:\/\/cucumber.io\/docs\/gherkin\/\" rel=\"noopener noreferrer nofollow\">Gherkin<\/a> \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u0440\u0430\u0441\u0438\u0432\u043e, \u043d\u043e \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043c\u0435\u0448\u0430\u044e\u0442 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438<\/p>\n<\/li>\n<li>\n<p> <strong>\u041c\u043d\u0438\u043c\u0430\u044f \u0432\u044b\u0433\u043e\u0434\u0430 \u0447\u0438\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/strong> \u2014 \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 <a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a> \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u0435\u0435, \u0447\u0435\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 <a href=\"https:\/\/cucumber.io\/docs\/gherkin\/\" rel=\"noopener noreferrer nofollow\">Gherkin<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 <strong>\u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u043c\u043e\u0449\u043d\u044b\u0435, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 API\/UI-\u0442\u0435\u0441\u0442\u044b \u2014 <\/strong><a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a><strong> \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0438\u0439 \u0432\u044b\u0431\u043e\u0440<\/strong>.<\/p>\n<h2>\u041c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 API <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/api.sampleapis.com\/fakebank\/accounts<\/strong><\/a>, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c <strong>\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>Pydantic<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <strong>\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/strong> JSON<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/networks\/#pydantic.networks.HttpUrl\" rel=\"noopener noreferrer nofollow\">HttpUrl<\/a> \u0438 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/networks\/#pydantic.networks.EmailStr\" rel=\"noopener noreferrer nofollow\">EmailStr<\/a>, \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 <strong>\u0443\u0434\u043e\u0431\u043d\u0443\u044e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e<\/strong><\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c:<\/p>\n<ol>\n<li>\n<p><code><strong>CreateOperationSchema<\/strong><\/code> \u2013 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p><code><strong>UpdateOperationSchema<\/strong><\/code> \u2013 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f <strong>PATCH<\/strong> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432)<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationSchema<\/strong><\/code> \u2013 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0441 id, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f <strong>\u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/strong><\/p>\n<\/li>\n<li>\n<p><code><strong>OperationsSchema<\/strong><\/code> \u2013 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"json\">{   \"id\": 25,   \"debit\": 6.99,   \"credit\": null,   \"category\": \"Merchandise\",   \"description\": \"Benderbr\u00e4u\",   \"transactionDate\": \"2016-02-25\" }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/schema\/operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/schema\/operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from datetime import date  from pydantic import BaseModel, Field, RootModel, ConfigDict   class CreateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.          \u041f\u043e\u043b\u044f:     - debit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e \u0441\u0447\u0451\u0442\u0430     - credit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0441\u0447\u0451\u0442     - category (str): \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - description (str): \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - transaction_date (date): \u0414\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (\u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)  # \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c alias \u043f\u0440\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438      debit: float | None     credit: float | None     category: str     description: str     transaction_date: date = Field(alias=\"transactionDate\")  # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c alias \u0434\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f API   class UpdateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 PATCH \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445).      \u0412\u0441\u0435 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445.      \u041f\u043e\u043b\u044f:     - debit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f     - credit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f     - category (str | None): \u041d\u043e\u0432\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f     - description (str | None): \u041d\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435     - transaction_date (date | None): \u041d\u043e\u0432\u0430\u044f \u0434\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (alias \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)      debit: float | None     credit: float | None     category: str | None      description: str | None     transaction_date: date | None = Field(alias=\"transactionDate\")   class OperationSchema(CreateOperationSchema):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0430\u044f ID.          \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 CreateOperationSchema \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435:     - id (int): \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     \"\"\"     id: int   class OperationsSchema(RootModel):     \"\"\"     \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.          \u041f\u043e\u043b\u0435:     - root (list[OperationSchema]): \u0421\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439     \"\"\"     root: list[OperationSchema] <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>CreateOperationSchema<\/strong><\/code> \u2013 <strong>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/strong>. \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0441\u0443\u043c\u043c\u044b \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f (<code>debit<\/code>), \u0441\u0443\u043c\u043c\u044b \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f (<code>credit<\/code>), \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 (<code>category<\/code>), \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f (<code>description<\/code>) \u0438 \u0434\u0430\u0442\u044b (<code>transaction_date<\/code>).<\/p>\n<\/li>\n<li>\n<p><code><strong>UpdateOperationSchema<\/strong><\/code> \u2013 <strong>\u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/strong>, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c <strong>\u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f<\/strong>. \u0412\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0434\u0435\u0441\u044c <strong>\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432\u0441\u0435\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationSchema<\/strong><\/code> \u2013 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f <code>CreateOperationSchema<\/code>, <strong>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435 <\/strong><code><strong>id<\/strong><\/code>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u043e\u0442\u0432\u0435\u0442\u0430\u0445 API.<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationsSchema<\/strong><\/code> \u2013 <strong>\u0441\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/strong>. \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/root_model\/#RootModel\" rel=\"noopener noreferrer nofollow\">RootModel<\/a>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 Pydantic.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 Pydantic, \u0430 \u043d\u0435 TypedDict \u0438\u043b\u0438 dataclasses?<\/h4>\n<p>Pydantic \u2013 \u044d\u0442\u043e <strong>\u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/strong> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API-\u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/peps.python.org\/pep-0589\/\" rel=\"noopener noreferrer nofollow\"><strong>TypedDict<\/strong><\/a> \u0438 <a href=\"https:\/\/docs.python.org\/3\/library\/collections.html\" rel=\"noopener noreferrer nofollow\"><strong>NamedTuple<\/strong><\/a> \u2013 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0434\u043b\u044f <strong>\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432<\/strong>, \u043d\u043e \u043d\u0435 \u0434\u043b\u044f <strong>\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/strong>.<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.python.org\/3\/library\/dataclasses.html\" rel=\"noopener noreferrer nofollow\"><strong>dataclasses<\/strong><\/a> \u2013 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043e\u0439, \u043d\u043e \u0443 \u043d\u0438\u0445 <strong>\u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 JSON<\/strong>. \u041e\u043d\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0445\u043e\u0440\u043e\u0448\u043e \u0432 <strong>\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u044f\u0445<\/strong>, \u043d\u043e \u0434\u043b\u044f API <strong>Pydantic \u2013 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>Pydantic \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435<\/strong>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>\u0430\u043b\u0438\u0430\u0441\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439<\/strong> \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 JSON <strong>\u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439<\/strong>.<\/p>\n<h2>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 API \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432. \u0427\u0442\u043e\u0431\u044b <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/strong> \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0438 <strong>\u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f<\/strong>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <\/strong><a href=\"https:\/\/faker.readthedocs.io\/en\/master\/\" rel=\"noopener noreferrer nofollow\"><strong>Faker<\/strong><\/a> \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043b\u0430\u0441\u0441 <code><strong>Fake<\/strong><\/code>. \u041a\u043b\u0430\u0441\u0441 <code><strong>Fake<\/strong><\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c <strong>\u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441<\/strong> \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0443\u0436\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/fakers.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/fakers.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from datetime import date, timedelta  from faker import Faker   class Fake:     \"\"\"     \u041a\u043b\u0430\u0441\u0441-\u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 Faker, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445     \u0434\u043b\u044f \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.         \"\"\"     def __init__(self, faker: Faker):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 Fake \u0441 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c Faker.          :param faker: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Faker \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.         \"\"\"         self.faker = faker      def date(self, start: timedelta = timedelta(days=-30), end: timedelta = timedelta()) -&gt; date:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0434\u0430\u0442\u0443 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.          :param start: \u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e -30 \u0434\u043d\u0435\u0439 \u043e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0430\u0442\u044b).         :param end: \u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u044f\u044f \u0434\u0430\u0442\u0430).         :return: \u0421\u043b\u0443\u0447\u0430\u0439\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.         \"\"\"         return self.faker.date_between(start_date=start, end_date=end)      def money(self, start: float = -100, end: float = 100) -&gt; float:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0443 \u0434\u0435\u043d\u0435\u0433.          :param start: \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e -100).         :param end: \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 100).         :return: \u0421\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0437\u0430\u043f\u044f\u0442\u043e\u0439 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.         \"\"\"         return self.faker.pyfloat(min_value=start, max_value=end)      def category(self) -&gt; str:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432.          :return: \u041e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 ('food', 'taxi', 'fuel' \u0438 \u0442.\u0434.).         \"\"\"         return self.faker.random_element(['food', 'taxi', 'fuel', 'beauty', 'restaurants'])      def sentence(self) -&gt; str:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.          :return: \u0421\u0442\u0440\u043e\u043a\u0430 \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c.         \"\"\"         return self.faker.sentence()   # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 `fake`, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u044f\u0445. fake = Fake(faker=Faker()) <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 <strong>Fake<\/strong> <strong>\u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u0443\u0435\u0442<\/strong> \u043b\u043e\u0433\u0438\u043a\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/faker.readthedocs.io\/en\/master\/\" rel=\"noopener noreferrer nofollow\">Faker<\/a> \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <strong>\u0443\u0434\u043e\u0431\u043d\u044b\u0439 API<\/strong> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u043c\u0435\u0441\u0442\u043e \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 <code>Faker().some_method()<\/code> \u0432 \u043a\u043e\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>fake.some_method()<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c <strong>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0435\u0439\u043a\u043e\u0432\u0443\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e<\/strong> \u043f\u0440\u044f\u043c\u043e \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 Pydantic, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/fields\/#default-values\" rel=\"noopener noreferrer nofollow\">default_factory<\/a>. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c<\/strong> \u043f\u043e\u043b\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/schema\/operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/schema\/operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from datetime import date  from pydantic import BaseModel, Field, RootModel, ConfigDict  from tools.fakers import fake   class CreateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.          \u041f\u043e\u043b\u044f:     - debit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e \u0441\u0447\u0451\u0442\u0430     - credit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0441\u0447\u0451\u0442     - category (str): \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - description (str): \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - transaction_date (date): \u0414\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (\u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)      debit: float | None = Field(default_factory=fake.money)  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e \u0441\u0447\u0451\u0442\u0430     credit: float | None = Field(default_factory=fake.money)  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0441\u0447\u0451\u0442     category: str = Field(default_factory=fake.category)  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438     description: str = Field(default_factory=fake.sentence)  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f     transaction_date: date = Field(alias=\"transactionDate\", default_factory=fake.date)  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0439 \u0434\u0430\u0442\u044b   class UpdateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 PATCH \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445).      \u0412\u0441\u0435 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445.      \u041f\u043e\u043b\u044f:     - debit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f     - credit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f     - category (str | None): \u041d\u043e\u0432\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f     - description (str | None): \u041d\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435     - transaction_date (date | None): \u041d\u043e\u0432\u0430\u044f \u0434\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (alias \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)      debit: float | None = Field(default_factory=fake.money)     credit: float | None = Field(default_factory=fake.money)     category: str | None = Field(default_factory=fake.category)     description: str | None = Field(default_factory=fake.sentence)     transaction_date: date | None = Field(alias=\"transactionDate\", default_factory=fake.date)   class OperationSchema(CreateOperationSchema):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0430\u044f ID.          \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 CreateOperationSchema \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435:     - id (int): \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     \"\"\"     id: int   class OperationsSchema(RootModel):     \"\"\"     \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.          \u041f\u043e\u043b\u0435:     - root (list[OperationSchema]): \u0421\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439     \"\"\"     root: list[OperationSchema]  <\/code><\/pre>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 <\/strong><a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/fields\/#default-values\" rel=\"noopener noreferrer nofollow\"><strong>default_factory<\/strong><\/a><strong>. <\/strong>\u041a\u0430\u0436\u0434\u043e\u0435 \u043f\u043e\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 <strong>\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/strong> \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043c\u043e\u0434\u0435\u043b\u0438. \u041f\u0440\u0438\u043c\u0435\u0440 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<pre><code class=\"python\">operation = CreateOperationSchema() print(operation)<\/code><\/pre>\n<p><strong>\u0412\u044b\u0432\u043e\u0434 (\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0435):<\/strong><\/p>\n<pre><code class=\"json\">{     \"debit\": -25.4,     \"credit\": 87.6,     \"category\": \"fuel\",     \"description\": \"Paid for fuel at a gas station.\",     \"transactionDate\": \"2025-03-30\" }<\/code><\/pre>\n<h2>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432<\/h2>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c <strong>\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434<\/strong> \u043a \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043b\u0435\u0433\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434.<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e <strong>URL API<\/strong> \u0438 <strong>\u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/strong>, \u043d\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u044d\u0442\u043e\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c.<\/p>\n<p>\u0414\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/pydantic_settings\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic Settings<\/strong><\/a> \u2014 \u0443\u0434\u043e\u0431\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/config.py\" rel=\"noopener noreferrer nofollow\"><strong>config.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from pydantic import BaseModel, HttpUrl from pydantic_settings import BaseSettings, SettingsConfigDict   class HTTPClientConfig(BaseModel):     \"\"\"     \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430.      \u041f\u043e\u043b\u044f:         url (HttpUrl): \u0411\u0430\u0437\u043e\u0432\u044b\u0439 URL \u0434\u043b\u044f API.         timeout (float): \u0422\u0430\u0439\u043c\u0430\u0443\u0442 \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445.     \"\"\"     url: HttpUrl     timeout: float      @property     def client_url(self) -&gt; str:         \"\"\"         \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 URL API \u0432 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435.         \"\"\"         return str(self.url)   class Settings(BaseSettings):     \"\"\"     \u0413\u043b\u0430\u0432\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430.      \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 `.env` \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b.      \u041f\u043e\u043b\u044f:         fake_bank_http_client (HTTPClientConfig): \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430.     \"\"\"     model_config = SettingsConfigDict(         env_file='.env',  # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 .env         env_file_encoding='utf-8',  # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0443 \u0444\u0430\u0439\u043b\u0430         env_nested_delimiter='.'  # \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 (FAKE_BANK_HTTP_CLIENT.URL)     )      fake_bank_http_client: HTTPClientConfig  # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 <\/code><\/pre>\n<ul>\n<li>\n<p><strong>\u041a\u043b\u0430\u0441\u0441 <\/strong><code><strong>HTTPClientConfig<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/base_model\/#BaseModel\" rel=\"noopener noreferrer nofollow\">BaseModel<\/a> (Pydantic).<\/p>\n<\/li>\n<li>\n<p>\u041e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 <strong>\u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/strong>:<\/p>\n<ul>\n<li>\n<p>url: <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/networks\/#pydantic.networks.HttpUrl\" rel=\"noopener noreferrer nofollow\">HttpUrl<\/a> \u2014 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 API (Pydantic \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442, \u0447\u0442\u043e \u044d\u0442\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 URL).<\/p>\n<\/li>\n<li>\n<p>timeout: float \u2014 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 <a href=\"https:\/\/docs.python.org\/3\/library\/functions.html#property\" rel=\"noopener noreferrer nofollow\">@property<\/a> <code>client_url<\/code>, \u0447\u0442\u043e\u0431\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c url \u0432 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041a\u043b\u0430\u0441\u0441 <\/strong><code><strong>Settings<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/pydantic_settings\/#pydantic_settings.BaseSettings\" rel=\"noopener noreferrer nofollow\">BaseSettings<\/a> (Pydantic Settings).<\/p>\n<\/li>\n<li>\n<p><code>model_config<\/code> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p><strong>\u0413\u0434\u0435 \u0438\u0441\u043a\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435<\/strong> (\u0438\u0437 \u0444\u0430\u0439\u043b\u0430 .env).<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0443 \u0444\u0430\u0439\u043b\u0430<\/strong> (utf-8).<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/strong> <code>(env_nested_delimiter='.')<\/code>.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><code>fake_bank_http_client: HTTPClientConfig<\/code> \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>.env<\/strong><\/p>\n<pre><code>FAKE_BANK_HTTP_CLIENT.URL=\"https:\/\/api.sampleapis.com\" FAKE_BANK_HTTP_CLIENT.TIMEOUT=100<\/code><\/pre>\n<ul>\n<li>\n<p><code>FAKE_BANK_HTTP_CLIENT.URL<\/code> \u2014 \u0430\u0434\u0440\u0435\u0441 API, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HTTP-\u043a\u043b\u0438\u0435\u043d\u0442.<\/p>\n<\/li>\n<li>\n<p><code>FAKE_BANK_HTTP_CLIENT.TIMEOUT<\/code> \u2014 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445).<\/p>\n<\/li>\n<li>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f <code>env_nested_delimiter='.'<\/code>, \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>.env<\/strong> \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 <strong>\u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/strong> \u0432\u043d\u0443\u0442\u0440\u0438 <code>Settings<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e <strong>\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c <\/strong><code><strong>Settings<\/strong><\/code><strong> \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e<\/strong>:<\/p>\n<pre><code class=\"python\">settings = Settings()  print(settings.fake_bank_http_client.client_url)  # \"https:\/\/api.sampleapis.com\" print(settings.fake_bank_http_client.timeout)     # 100<\/code><\/pre>\n<p><strong>\u0412\u0430\u0436\u043d\u043e!<\/strong> \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>settings<\/code> \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c <strong>\u043d\u0435 \u0431\u0443\u0434\u0435\u043c<\/strong> \u2014 \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>settings<\/code> <strong>\u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440.<\/strong><\/p>\n<h2>API \u043a\u043b\u0438\u0435\u043d\u0442\u044b<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c API \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\">https:\/\/api.sampleapis.com\/fakebank\/accounts<\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 API \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/www.python-httpx.org\/advanced\/clients\/\" rel=\"noopener noreferrer nofollow\">httpx.Client<\/a>.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/clients\/base_client.py\" rel=\"noopener noreferrer nofollow\"><strong>\/clients\/base_client.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from typing import Any  import allure from httpx import Client, URL, Response, QueryParams from httpx._types import RequestData, RequestFiles  from config import HTTPClientConfig   class BaseClient:     \"\"\"     \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.      \u042d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432      (GET, POST, PATCH, DELETE) \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 httpx.Client \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f      \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c allure \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438      \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u043e \u0442\u0435\u0441\u0442\u0430\u0445.     \"\"\"        def __init__(self, client: Client):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430.         :param client: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 httpx.Client         \"\"\"         self.client = client      @allure.step(\"Make GET request to {url}\")     def get(self, url: URL | str, params: QueryParams | None = None) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param params: Query \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.get(url, params=params)      @allure.step(\"Make POST request to {url}\")     def post(             self,             url: URL | str,             json: Any | None = None,             data: RequestData | None = None,             files: RequestFiles | None = None     ) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param json: JSON \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :param data: \u0414\u0430\u043d\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u044b         :param files: \u0424\u0430\u0439\u043b\u044b \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.post(url, json=json, data=data, files=files)      @allure.step(\"Make PATCH request to {url}\")     def patch(self, url: URL | str, json: Any | None = None) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 PATCH-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param json: JSON \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.patch(url, json=json)      @allure.step(\"Make DELETE request to {url}\")     def delete(self, url: URL | str) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 DELETE-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.delete(url)   def get_http_client(config: HTTPClientConfig) -&gt; Client:     \"\"\"     \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430.      :param config: \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430     :return: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 httpx.Client     \"\"\"     return Client(         timeout=config.timeout,  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432         base_url=config.client_url,  # \u0411\u0430\u0437\u043e\u0432\u044b\u0439 URL \u0434\u043b\u044f API     ) <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>BaseClient<\/strong><\/code> \u2014 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u0443\u0435\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 HTTP-\u043c\u0435\u0442\u043e\u0434\u044b (GET, POST, PATCH, DELETE) \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 API. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <a href=\"https:\/\/allurereport.org\/docs\/pytest\/#divide-a-test-into-steps\" rel=\"noopener noreferrer nofollow\">allure.step<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0448\u0430\u0433\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u043e\u0442\u0447\u0435\u0442\u0435.<\/p>\n<\/li>\n<li>\n<p><code><strong>get_http_client<\/strong><\/code> \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 <a href=\"https:\/\/www.python-httpx.org\/advanced\/clients\/\" rel=\"noopener noreferrer nofollow\">httpx.Client<\/a> \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, URL \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442), \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0412\u0430\u0436\u043d\u043e!<\/strong> \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 URI \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <a href=\"https:\/\/docs.python.org\/3\/library\/enum.html\" rel=\"noopener noreferrer nofollow\">Enum<\/a>. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441\u0430\u043c\u0438 \u0438 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u043f\u0435\u0447\u0430\u0442\u043e\u043a.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/routes.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/routes.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from enum import Enum   class APIRoutes(str, Enum):     \"\"\"     \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 URI-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 API \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430.      \u042d\u0442\u043e \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0441\u0435\u043c\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u043c\u0438 API,      \u0447\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u0440\u0438 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0438 \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.     \"\"\"     CARDS = \"\/fakebank\/cards\"     CLIENTS = \"\/fakebank\/clients\"     OPERATIONS = \"\/fakebank\/accounts\"  # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 URI \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438     STATEMENTS = \"\/fakebank\/statements\"     NOTIFICATIONS = \"\/fakebank\/notifications\"      def __str__(self):         return self.value <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>APIRoutes<\/strong><\/code> \u2014 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0430\u0434\u0440\u0435\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0432\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b, \u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c API \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f API <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\">https:\/\/api.sampleapis.com\/fakebank\/accounts<\/a>. \u041e\u043f\u0438\u0448\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u0432 API:<\/p>\n<ul>\n<li>\n<p><code><strong>get_operations_api<\/strong><\/code> \u2014 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><code><strong>get_operation_api<\/strong><\/code> \u2014 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e <code>operation_id<\/code>.<\/p>\n<\/li>\n<li>\n<p><code><strong>create_operation_api<\/strong><\/code> \u2014 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0438 \u0432\u0435\u0440\u043d\u0435\u0442 \u0435\u0451 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<li>\n<p><code><strong>update_operation_api<\/strong><\/code> \u2014 \u043e\u0431\u043d\u043e\u0432\u0438\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u043e <code>operation_id<\/code> \u0438 \u0432\u0435\u0440\u043d\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<li>\n<p><code><strong>delete_operation_api<\/strong><\/code> \u2014 \u0443\u0434\u0430\u043b\u0438\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u043e <code>operation_id<\/code><\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/clients\/operations_client.py\" rel=\"noopener noreferrer nofollow\"><strong>\/clients\/operations_client.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">import allure from httpx import Response  from clients.base_client import BaseClient, get_http_client from config import Settings from schema.operations import CreateOperationSchema, UpdateOperationSchema, OperationSchema from tools.routes import APIRoutes   class OperationsClient(BaseClient):     \"\"\"     \u041a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.     \"\"\"        @allure.step(\"Get list of operations\")     def get_operations_api(self) -&gt; Response:         \"\"\"         \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.                  :return: \u041e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0432\u0441\u0435\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445.         \"\"\"         return self.get(APIRoutes.OPERATIONS)      @allure.step(\"Get operation by id {operation_id}\")     def get_operation_api(self, operation_id: int) -&gt; Response:         \"\"\"         \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443.          :param operation_id: \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         :return: \u041e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e\u0431 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         \"\"\"         return self.get(f\"{APIRoutes.OPERATIONS}\/{operation_id}\")      @allure.step(\"Create operation\")     def create_operation_api(self, operation: CreateOperationSchema) -&gt; Response:         \"\"\"         \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e.          :param operation: \u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         :return: \u041e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         \"\"\"         return self.post(             APIRoutes.OPERATIONS,             json=operation.model_dump(mode='json', by_alias=True)  # \u0421\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 JSON \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439         )      @allure.step(\"Update operation by id {operation_id}\")     def update_operation_api(             self,             operation_id: int,             operation: UpdateOperationSchema     ) -&gt; Response:         \"\"\"         \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443.          :param operation_id: \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c.         :param operation: \u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         :return: \u041e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         \"\"\"         return self.patch(             f\"{APIRoutes.OPERATIONS}\/{operation_id}\",              json=operation.model_dump(mode='json', by_alias=True, exclude_none=True)         )      @allure.step(\"Delete operation by id {operation_id}\")     def delete_operation_api(self, operation_id: int) -&gt; Response:         \"\"\"         \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443.          :param operation_id: \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c.         :return: \u041e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.         \"\"\"         return self.delete(f\"{APIRoutes.OPERATIONS}\/{operation_id}\")      def create_operation(self) -&gt; OperationSchema:         \"\"\"         \u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.          \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0445\u0435\u043c\u044b `CreateOperationSchema`, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441         \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 `OperationSchema`.          :return: \u041e\u0431\u044a\u0435\u043a\u0442 `OperationSchema`, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e.         \"\"\"         # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430)         request = CreateOperationSchema()         # \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435         response = self.create_operation_api(request)         # \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043a\u0430\u043a \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0445\u0435\u043c\u044b         return OperationSchema.model_validate_json(response.text)   def get_operations_client(settings: Settings) -&gt; OperationsClient:     \"\"\"     \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 OperationsClient \u0441 \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.      :param settings: \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API.     :return: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.     \"\"\"     return OperationsClient(client=get_http_client(settings.fake_bank_http_client)) <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>OperationsClient<\/strong><\/code> \u2014 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043e\u0442 <code>BaseClient<\/code> \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438 (\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438).<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0430\u043d\u043d\u043e\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>@allure.step<\/code>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0448\u0430\u0433\u0438 \u0432 \u043e\u0442\u0447\u0435\u0442 Allure. \u042d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0448\u0430\u0433\u043e\u0432 \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 JSON \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/serialization\/#modelmodel_dump\" rel=\"noopener noreferrer nofollow\">model_dump<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0430\u043b\u0438\u0430\u0441\u044b \u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u043e\u043b\u044f \u0441 None \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><code><strong>get_operations_client<\/strong><\/code> \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 <code>OperationsClient<\/code>. \u041e\u043d\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0438\u0445 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0441 \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><code><strong>get_http_client<\/strong><\/code> \u2014 \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <a href=\"https:\/\/www.python-httpx.org\/advanced\/clients\/\" rel=\"noopener noreferrer nofollow\">httpx.Client<\/a> \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0412\u0430\u0436\u043d\u043e!<\/strong> \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e <a href=\"https:\/\/allurereport.org\/docs\/pytest\/#divide-a-test-into-steps\" rel=\"noopener noreferrer nofollow\">\u0448\u0430\u0433\u0438<\/a> \u0434\u043b\u044f <strong>Allure<\/strong> \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u0430 \u0434\u0432\u0443\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445: \u0434\u043b\u044f <code>BaseClient<\/code> \u0438 <code>OperationsClient<\/code>.<\/p>\n<ol>\n<li>\n<p><strong>\u0428\u0430\u0433\u0438 \u0434\u043b\u044f BaseClient<\/strong>: \u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 <code>BaseClient<\/code> \u0448\u0430\u0433\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u043e\u0439 HTTP-\u043c\u0435\u0442\u043e\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, GET, POST, PATCH, DELETE), \u043a\u0443\u0434\u0430 \u0431\u044b\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u0437\u0430\u043f\u0440\u043e\u0441, \u0441 \u043a\u0430\u043a\u0438\u043c \u0442\u0435\u043b\u043e\u043c \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0442\u043e\u0447\u043d\u043e \u0432\u0438\u0434\u0435\u0442\u044c \u0432\u0441\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u0437\u043d\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 HTTP-\u043c\u0435\u0442\u043e\u0434.<\/p>\n<\/li>\n<li>\n<p>URL, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0431\u044b\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<\/li>\n<li>\n<p>\u0422\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u0431\u044b\u043b\u043e).<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0428\u0430\u0433\u0438 \u0434\u043b\u044f OperationsClient<\/strong>: \u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 <code>OperationsClient<\/code> \u0448\u0430\u0433\u0438 \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f <strong>\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438<\/strong>. \u0417\u0434\u0435\u0441\u044c \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0430\u043c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441), \u0430 \u0441\u043a\u043e\u0440\u0435\u0435 \u0431\u0438\u0437\u043d\u0435\u0441\u043e\u0432\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a &#171;\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439&#187;, &#171;\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438&#187; \u0438 \u0442.\u0434. \u042d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/9\/index.html#suites\/f1af232b694e6dfae974e19eaa3c335a\/90056ece25555bd\/\" rel=\"noopener noreferrer nofollow\">\u043e\u0442\u0447\u0435\u0442 <strong>Allure<\/strong><\/a> \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c \u0438 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u043d\u0435 \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0430\u044f \u0435\u0433\u043e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0434\u0435\u0442\u0430\u043b\u044f\u043c\u0438.<\/p>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0438\u043b\u0438 \u043a\u0430\u043a\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c), \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u043a\u0440\u044b\u0442\u044c \u0448\u0430\u0433\u0438 \u0441 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c, \u0433\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432\u0441\u0435 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0434\u0435\u0442\u0430\u043b\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <\/strong><a href=\"https:\/\/allurereport.org\/docs\/pytest\/#divide-a-test-into-steps\" rel=\"noopener noreferrer nofollow\"><strong>@allure.step<\/strong><\/a>: \u0412\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <\/strong><a href=\"https:\/\/allurereport.org\/docs\/pytest\/#divide-a-test-into-steps\" rel=\"noopener noreferrer nofollow\"><strong>@allure.step<\/strong><\/a> \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432 \u043e\u0442\u0447\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u044f\u043b\u0438\u0441\u044c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0435 \u0432 \u043c\u0435\u0442\u043e\u0434\u044b \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0432 \u043e\u0442\u0447\u0435\u0442\u0435 \u0432\u0438\u0434\u0435\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e, \u043a\u0430\u043a\u0438\u0435 \u0448\u0430\u0433\u0438 \u0431\u044b\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b, \u043d\u043e \u0438 \u043a\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u043f\u043e\u043b\u043d\u0443\u044e \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0432\u0441\u0435\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043c\u044b \u0434\u043e\u0441\u0442\u0438\u0433\u0430\u0435\u043c:<\/p>\n<ul>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u0438<\/strong> \u0432 \u043e\u0442\u0447\u0435\u0442\u0435, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 on-demand.<\/p>\n<\/li>\n<li>\n<p><strong>\u0427\u0438\u0441\u0442\u043e\u0442\u044b \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u043e\u0441\u0442\u0438<\/strong> \u043e\u0442\u0447\u0435\u0442\u0430, \u0433\u0434\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1a0\/716\/b0f\/1a0716b0f1a3468a5d408d73215536f9.png\" width=\"1328\" height=\"824\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a0\/716\/b0f\/1a0716b0f1a3468a5d408d73215536f9.png\"\/><\/figure>\n<h2>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441 API<\/h2>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u043b\u043e\u0433\u043e\u0432 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043d\u0430 CI\/CD, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u043e\u0442\u043b\u0430\u0434\u043a\u0435, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043e\u0442\u0432\u0435\u0442\u043e\u0432. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442:<\/p>\n<ul>\n<li>\n<p>\u0412\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f (\u043c\u0435\u0442\u043e\u0434, URL).<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u043e\u0442\u0432\u0435\u0442\u0430 \u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u0435, \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0441\u044f \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0410\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 API \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u0435\u0431\u0430\u0433-\u0440\u0435\u0436\u0438\u043c \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0442\u0440\u0430\u0441\u0441\u0438\u0440\u043e\u0432\u043a\u0438 \u0441\u0435\u0442\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0432\u0430\u0436\u043d\u043e <strong>\u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0441\u043e\u0440\u044f\u0442\u044c \u043b\u043e\u0433\u0438 \u043d\u0435\u043d\u0443\u0436\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043c\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0437\u0430\u043f\u0438\u0441\u044f\u043c\u0438:<\/p>\n<ul>\n<li>\n<p><strong>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/strong>: \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c HTTP-\u043c\u0435\u0442\u043e\u0434 \u0438 URL.<\/p>\n<\/li>\n<li>\n<p><strong>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u0432\u0435\u0442\u0430<\/strong>: \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434, \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (reason phrase) \u0438 URL.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 QA Automation \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442 <a href=\"https:\/\/curl.se\/\" rel=\"noopener noreferrer nofollow\"><strong>cURL-\u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/strong><\/a> \u0432 \u043b\u043e\u0433\u0438 \u0434\u043b\u044f \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043d\u043e \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0438\u0437\u043b\u0438\u0448\u043d\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043c \u043b\u043e\u0433\u043e\u0432. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c <strong>\u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u044f\u0442\u044c <\/strong><a href=\"https:\/\/curl.se\/\" rel=\"noopener noreferrer nofollow\"><strong>cURL-\u043a\u043e\u043c\u0430\u043d\u0434\u0443<\/strong><\/a><strong> \u0432 <\/strong><a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/9\/index.html\" rel=\"noopener noreferrer nofollow\"><strong>Allure-\u043e\u0442\u0447\u0435\u0442<\/strong><\/a>, \u0433\u0434\u0435 \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0448\u0430\u0433\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <strong>\u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0431\u0438\u043b\u0434\u0435\u0440<\/strong> \u0434\u043b\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043b\u043e\u0433\u0433\u0435\u0440\u0430 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/logger.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/logger.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">import logging   def get_logger(name: str) -&gt; logging.Logger:     \"\"\"     \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u043b\u043e\u0433\u0433\u0435\u0440 \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c \u0438\u043c\u0435\u043d\u0435\u043c.      :param name: \u0418\u043c\u044f \u043b\u043e\u0433\u0433\u0435\u0440\u0430.     :return: \u041e\u0431\u044a\u0435\u043a\u0442 \u043b\u043e\u0433\u0433\u0435\u0440\u0430.     \"\"\"     logger = logging.getLogger(name)     logger.setLevel(logging.DEBUG)  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f      handler = logging.StreamHandler()  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u044b\u0432\u043e\u0434\u0430 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c     handler.setLevel(logging.DEBUG)  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430      # \u0424\u043e\u0440\u043c\u0430\u0442 \u043b\u043e\u0433\u043e\u0432: \u0432\u0440\u0435\u043c\u044f | \u0438\u043c\u044f \u043b\u043e\u0433\u0433\u0435\u0440\u0430 | \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f | \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435     formatter = logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s')     handler.setFormatter(formatter)      logger.addHandler(handler)  # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a \u043b\u043e\u0433\u0433\u0435\u0440\u0443      return logger<\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>get_logger(name: str) -&gt; logging.Logger<\/strong><\/code> \u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043b\u043e\u0433\u0433\u0435\u0440 \u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0440\u043e\u0432\u0435\u043d\u044c <\/strong><code><strong>DEBUG<\/strong><\/code> \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0432\u0438\u0434\u0435\u0442\u044c \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u044b\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <\/strong><code><strong>StreamHandler()<\/strong><\/code> \u2014 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u043b\u043e\u0433\u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c.<\/p>\n<\/li>\n<li>\n<p><strong>\u0424\u043e\u0440\u043c\u0430\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/strong> \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0412\u0440\u0435\u043c\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0418\u043c\u044f \u043b\u043e\u0433\u0433\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u0423\u0440\u043e\u0432\u0435\u043d\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0433\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043b\u044e\u0431\u043e\u043c \u0444\u0430\u0439\u043b\u0435, \u0432\u044b\u0437\u044b\u0432\u0430\u044f <code>get_logger(\"\u0418\u043c\u044f\")<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c <a href=\"https:\/\/www.python-httpx.org\/advanced\/event-hooks\/\" rel=\"noopener noreferrer nofollow\"><strong>event hooks<\/strong><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f <strong>\u0434\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430<\/strong>. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u044d\u0442\u043e\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/clients\/event_hooks.py\" rel=\"noopener noreferrer nofollow\"><strong>\/clients\/event_hooks.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from httpx import Request, Response  from tools.logger import get_logger  # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043b\u043e\u0433\u0433\u0435\u0440 \u0434\u043b\u044f HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 logger = get_logger(\"HTTP_CLIENT\")   def log_request_event_hook(request: Request):     \"\"\"     \u041b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.      :param request: \u041e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 HTTPX.     \"\"\"     logger.info(f'Make {request.method} request to {request.url}')   def log_response_event_hook(response: Response):     \"\"\"     \u041b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f HTTP-\u043e\u0442\u0432\u0435\u0442\u0430.      :param response: \u041e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 HTTPX.     \"\"\"     logger.info(         f\"Got response {response.status_code} {response.reason_phrase} from {response.url}\"     ) <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>log_request_event_hook(request: Request)<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 HTTP-\u043c\u0435\u0442\u043e\u0434 (GET, POST \u0438 \u0442. \u0434.) \u0438 URL \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><code><strong>log_response_event_hook(response: Response)<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 HTTP-\u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434, \u043f\u0440\u0438\u0447\u0438\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0430 (reason_phrase) \u0438 URL \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u041e\u0431\u0430 \u0445\u0443\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u043a \u043a\u043b\u0438\u0435\u043d\u0442\u0443 <strong>HTTPX<\/strong> \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0445\u0443\u043a-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043a HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0443, \u0447\u0442\u043e\u0431\u044b \u043b\u043e\u0433\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0438\u0441\u0430\u043b\u0438\u0441\u044c \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/clients\/base_client.py\" rel=\"noopener noreferrer nofollow\"><strong>\/clients\/base_client.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from typing import Any  import allure from httpx import Client, URL, Response, QueryParams from httpx._types import RequestData, RequestFiles  from config import HTTPClientConfig   class BaseClient:     \"\"\"     \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.      \u042d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432      (GET, POST, PATCH, DELETE) \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 httpx.Client \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f      \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c allure \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438      \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u043e \u0442\u0435\u0441\u0442\u0430\u0445.     \"\"\"        def __init__(self, client: Client):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430.         :param client: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 httpx.Client         \"\"\"         self.client = client      @allure.step(\"Make GET request to {url}\")     def get(self, url: URL | str, params: QueryParams | None = None) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param params: Query \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.get(url, params=params)      @allure.step(\"Make POST request to {url}\")     def post(             self,             url: URL | str,             json: Any | None = None,             data: RequestData | None = None,             files: RequestFiles | None = None     ) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 POST-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param json: JSON \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :param data: \u0414\u0430\u043d\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u044b         :param files: \u0424\u0430\u0439\u043b\u044b \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.post(url, json=json, data=data, files=files)      @allure.step(\"Make PATCH request to {url}\")     def patch(self, url: URL | str, json: Any | None = None) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 PATCH-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :param json: JSON \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.patch(url, json=json)      @allure.step(\"Make DELETE request to {url}\")     def delete(self, url: URL | str) -&gt; Response:         \"\"\"         \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 DELETE-\u0437\u0430\u043f\u0440\u043e\u0441.          :param url: URL \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430         :return: HTTP-\u043e\u0442\u0432\u0435\u0442         \"\"\"         return self.client.delete(url)   def get_http_client(config: HTTPClientConfig) -&gt; Client:     \"\"\"     \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430.      :param config: \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430     :return: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 httpx.Client     \"\"\"     return Client(         timeout=config.timeout,         base_url=config.client_url,         event_hooks={             \"request\": [log_request_event_hook],  # \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c             \"response\": [log_response_event_hook]  # \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u0432\u0435\u0442\u0430         }     ) <\/code><\/pre>\n<ul>\n<li>\n<p><strong>\u0412 <\/strong><code><strong>BaseClient<\/strong><\/code><strong> \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u043b\u043e\u0441\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/strong> \u2013 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e <a href=\"https:\/\/www.python-httpx.org\/advanced\/event-hooks\/\" rel=\"noopener noreferrer nofollow\">event hooks<\/a>.<\/p>\n<\/li>\n<li>\n<p>\u0412 <code><strong>get_http_client(config: HTTPClientConfig<\/strong><\/code><strong>)<\/strong>:<\/p>\n<ul>\n<li>\n<p><code>event_hooks[\"request\"] = [log_request_event_hook]<\/code> \u2192 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p><code>event_hooks[\"response\"] = [log_response_event_hook]<\/code> \u2192 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c <strong>\u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0438 \u043e\u0442\u0432\u0435\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u043b\u043e\u0433\u0430\u0445<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<pre><code>2025-03-30 13:38:06,646 | HTTP_CLIENT | INFO | Make GET request to https:\/\/api.sampleapis.com\/fakebank\/accounts\/194 2025-03-30 13:38:07,023 | HTTP_CLIENT | INFO | Got response 200 OK from https:\/\/api.sampleapis.com\/fakebank\/accounts\/194<\/code><\/pre>\n<h2>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/h2>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432. \u041d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b:<\/p>\n<ul>\n<li>\n<p><code><strong>function_operation<\/strong><\/code> \u2013 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043d\u043e\u0432\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u0435\u0451 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c, \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0438\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432 \u0442\u0435\u0441\u0442\u0435.<\/p>\n<\/li>\n<li>\n<p><code><strong>operations_client<\/strong><\/code> \u2013 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 API-\u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c.<\/p>\n<\/li>\n<li>\n<p><code><strong>settings<\/strong><\/code> \u2013 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043d\u0430 \u0432\u0441\u044e \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 Pytest \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u0430 \u043d\u0435 conftest.py?<\/h4>\n<p>\u0414\u043b\u044f \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/docs.pytest.org\/en\/7.1.x\/how-to\/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file\" rel=\"noopener noreferrer nofollow\"><strong>Pytest \u043f\u043b\u0430\u0433\u0438\u043d\u044b<\/strong><\/a>. \u041f\u043b\u0430\u0433\u0438\u043d\u044b \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0438 \u0433\u0438\u0431\u0447\u0435, \u0447\u0435\u043c \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u0438\u043a\u0441\u0442\u0443\u0440 \u0432 <a href=\"https:\/\/docs.pytest.org\/en\/stable\/reference\/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files\" rel=\"noopener noreferrer nofollow\"><strong>conftest.py<\/strong><\/a>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e:<\/p>\n<ul>\n<li>\n<p>\u041d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u0431\u043e\u0442\u0438\u0442\u044c\u0441\u044f \u043e \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 <strong>conftest.py<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u0438\u0437 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0432\u043e \u0432\u0441\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>conftest.py<\/strong> \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0433\u0440\u0443\u043f\u043f \u0442\u0435\u0441\u0442\u043e\u0432. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0444\u0430\u0439\u043b\u044b <strong>conftest.py<\/strong> \u0440\u0430\u0437\u0440\u0430\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0434\u043e 1000+ \u0441\u0442\u0440\u043e\u043a, \u0447\u0442\u043e \u0437\u0430\u0442\u0440\u0443\u0434\u043d\u044f\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443.<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/fixtures\/settings.py\" rel=\"noopener noreferrer nofollow\"><strong>\/fixtures\/settings.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">import pytest  from config import Settings   @pytest.fixture(scope=\"session\") def settings() -&gt; Settings:     \"\"\"     \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043d\u0430 \u0432\u0441\u044e \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e.          :return: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 Settings \u0441 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u043c\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043c\u0438.     \"\"\"     return Settings()<\/code><\/pre>\n<ul>\n<li>\n<p><code>@pytest.fixture(scope=\"session\")<\/code> \u2013 \u0444\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u0442, \u0447\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f <strong>\u043e\u0434\u0438\u043d \u0440\u0430\u0437<\/strong> \u0437\u0430 \u0432\u0441\u044e \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p><code>settings()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 <code>Settings<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u0445 \u0438 \u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0435\u0441\u0442\u0435.<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/fixtures\/operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/fixtures\/operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">import pytest  from clients.operations_client import OperationsClient, get_operations_client from config import Settings from schema.operations import OperationSchema   @pytest.fixture def operations_client(settings: Settings) -&gt; OperationsClient:     \"\"\"     \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 API-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.          :param settings: \u041e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0438.     :return: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 OperationsClient.     \"\"\"     return get_operations_client(settings)   @pytest.fixture def function_operation(operations_client: OperationsClient) -&gt; OperationSchema:     \"\"\"     \u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u0441\u0442\u043e\u043c \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0435\u0451 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u0430.          :param operations_client: API-\u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.     :return: \u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f.     \"\"\"     operation = operations_client.create_operation()     yield operation      operations_client.delete_operation_api(operation.id) <\/code><\/pre>\n<ul>\n<li>\n<p><code>operations_client(settings: Settings)<\/code><\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u0442 <strong>\u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 API-\u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/strong> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>settings<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0438\u0445 \u0432 <code>get_operations_client()<\/code>.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><code>function_operation(operations_client: OperationsClient)<\/code><\/p>\n<ul>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u0451\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e<\/strong> \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u0441\u0442\u043e\u043c (<code>operations_client.create_operation()<\/code>).<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0435\u0451 \u0432 \u0442\u0435\u0441\u0442<\/strong> \u0447\u0435\u0440\u0435\u0437 <code>yield<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e<\/strong> \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u0430 (<code>operations_client.delete_operation_api(operation.id)<\/code>).<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 function_operation, \u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e operation?<\/h4>\n<p>\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>\u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u044f {scope}_{entity}<\/strong>, \u0433\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><code>function_<\/code> \u2013 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f <strong>\u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430<\/strong>.<\/p>\n<\/li>\n<li>\n<p><code>_operation<\/code> \u2013 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u0447\u0442\u043e \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0430\u044f \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0441 \u0443\u0440\u043e\u0432\u043d\u0435\u043c class, \u0435\u0451 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c <code>class_operation<\/code>, \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h4>\u041d\u0443\u0436\u043d\u043e \u043b\u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435?<\/h4>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043e, <strong>\u0435\u0441\u043b\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c<\/strong>. \u041e\u0434\u043d\u0430\u043a\u043e, \u0435\u0441\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f <strong>\u0440\u0443\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f <strong>\u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0442\u043e \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0438\u0445 <strong>\u043d\u0435 \u0441\u0442\u043e\u0438\u0442<\/strong>.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u0441 \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438 \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/conftest.py\" rel=\"noopener noreferrer nofollow\"><strong>conftest.py<\/strong><\/a>, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u043b\u0438\u0441\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438:<\/p>\n<pre><code class=\"python\">pytest_plugins = (     \"fixtures.settings\",     \"fixtures.operations\" ) <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b <code>settings<\/code>, <code>operations_client<\/code> \u0438 <code>function_operation<\/code> \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0432\u043e \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<h2>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f JSON \u0441\u0445\u0435\u043c\u044b<\/h2>\n<h4>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f JSON-\u0441\u0445\u0435\u043c\u044b?<\/h4>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 API \u0432\u0430\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0439 JSON-\u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0443. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p>\u0411\u044b\u0441\u0442\u0440\u043e \u0432\u044b\u044f\u0432\u043b\u044f\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 API, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0441\u043b\u043e\u043c\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u044e\u0442 \u043d\u0443\u0436\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0449\u0438\u0442\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043b \u043e\u0448\u0438\u0431\u043a\u0443 \u0438 API \u0441\u0442\u0430\u043b\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443, \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\">jsonschema<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c JSON-\u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c \u0441\u0445\u0435\u043c\u0430\u043c.<\/p>\n<h4>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 JSON-\u0441\u0445\u0435\u043c\u044b<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e JSON-\u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0439 <a href=\"https:\/\/json-schema.org\/\" rel=\"noopener noreferrer nofollow\">JSON-\u0441\u0445\u0435\u043c\u0435<\/a>.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/assertions\/schema.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/assertions\/schema.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from typing import Any  import allure from jsonschema import validate from jsonschema.validators import Draft202012Validator  from tools.logger import get_logger  # \u041b\u043e\u0433\u0433\u0435\u0440 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 logger = get_logger(\"SCHEMA_ASSERTIONS\")   @allure.step(\"Validating JSON schema\") def validate_json_schema(instance: Any, schema: dict) -&gt; None:     \"\"\"\"     \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 JSON-\u043e\u0431\u044a\u0435\u043a\u0442 (instance) \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 JSON-\u0441\u0445\u0435\u043c\u0435 (schema).      :param: instance (Any): JSON-\u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c.     :param: schema (dict): JSON-\u0441\u0445\u0435\u043c\u0430, \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f.     :raises:         jsonschema.exceptions.ValidationError: \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f JSON-\u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0445\u0435\u043c\u0435.         jsonschema.exceptions.SchemaError: \u0415\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430.     \"\"\"     logger.info(\"Validating JSON schema\")      # \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e JSON-\u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0445\u0435\u043c\u0435     validate(         schema=schema,  # JSON-\u0441\u0445\u0435\u043c\u0430, \u0437\u0430\u0434\u0430\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445         instance=instance,  # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c\u044b\u0439 JSON-\u043e\u0431\u044a\u0435\u043a\u0442         format_checker=Draft202012Validator.FORMAT_CHECKER,  # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, email, \u0434\u0430\u0442\u0430 \u0438 \u0442.\u0434.)     ) <\/code><\/pre>\n<ol>\n<li>\n<p><strong>\u0418\u043c\u043f\u043e\u0440\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/latest\/validate\/#the-basics\" rel=\"noopener noreferrer nofollow\">jsonschema.validate<\/a> \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 JSON-\u043e\u0431\u044a\u0435\u043a\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/latest\/validate\/#validating-formats\" rel=\"noopener noreferrer nofollow\">Draft202012Validator.FORMAT_CHECKER<\/a> \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, email, \u0434\u0430\u0442\u0430 \u0438 \u0442. \u0434.).<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <\/strong><code><strong>validate_json_schema<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 JSON-\u043e\u0431\u044a\u0435\u043a\u0442 (instance) \u0438 JSON-\u0441\u0445\u0435\u043c\u0443 (schema).<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>validate()<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u043e\u0431\u044a\u0435\u043a\u0442 \u0438 \u0441\u0445\u0435\u043c\u0443.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/strong><\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u0445\u0435\u043c\u0435, <a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\">jsonschema<\/a> \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u0442 <a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/latest\/api\/jsonschema\/exceptions\/#jsonschema.exceptions.ValidationError\" rel=\"noopener noreferrer nofollow\"><strong>ValidationError<\/strong><\/a>.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0430\u043c\u0430 \u0441\u0445\u0435\u043c\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0438, \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d <a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/latest\/api\/jsonschema\/exceptions\/#jsonschema.exceptions.SchemaError\" rel=\"noopener noreferrer nofollow\"><strong>SchemaError<\/strong><\/a>.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u0442\u0430\u043a\u0443\u044e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430\u043c\u0438 API \u0438 \u0437\u0430\u0449\u0438\u0449\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439. \u042d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0440\u0435\u0433\u0440\u0435\u0441\u0441\u0438\u0438.<\/p>\n<h2>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/h2>\n<h4>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438?<\/h4>\n<p>\u041f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 API \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0435 \u0442\u043e\u0447\u043a\u0438, \u043d\u043e \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a.<\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u0430.<\/p>\n<h4>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u044b \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438?<\/h4>\n<ol>\n<li>\n<p><strong>\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 Allure-\u043e\u0442\u0447\u0451\u0442\u0430\u0445<\/strong><\/p>\n<ul>\n<li>\n<p>\u0412\u0441\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u0438\u0434\u043d\u044b \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0448\u0430\u0433\u0438 \u0432 \u043e\u0442\u0447\u0451\u0442\u0435, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442 \u0430\u043d\u0430\u043b\u0438\u0437 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a<\/strong><\/p>\n<ul>\n<li>\n<p>\u041a\u0430\u0436\u0434\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0432 \u043e\u0442\u043b\u0430\u0434\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043a\u0430\u043a \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u0442\u0430\u043a \u0438 \u043d\u0430 CI.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0421\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442-\u043a\u043e\u0434\u0430<\/strong><\/p>\n<ul>\n<li>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0438\u0441\u0430\u0442\u044c <code>assert actual == expected, \"\u041e\u0448\u0438\u0431\u043a\u0430 ...\"<\/code>, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u043c\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h4>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0434\u0432\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438:<\/p>\n<ul>\n<li>\n<p><code>assert_status_code<\/code> \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u043c\u0443.<\/p>\n<\/li>\n<li>\n<p><code>assert_equal<\/code> \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0434\u0432\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0432\u043d\u044b.<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/assertions\/base.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/assertions\/base.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from typing import Any  import allure  from tools.logger import get_logger  logger = get_logger(\"BASE_ASSERTIONS\")   @allure.step(\"Check that response status code equals to {expected}\") def assert_status_code(actual: int, expected: int):     \"\"\"     \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e HTTP-\u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u043c\u0443.      :param: actual (int): \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434.     :param: expected (int): \u041e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434.     :raises: AssertionError: \u0415\u0441\u043b\u0438 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u044b \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442.     \"\"\"     logger.info(f\"Check that response status code equals to {expected}\")      assert actual == expected, (         f'Incorrect response status code. '         f'Expected status code: {expected}. '         f'Actual status code: {actual}'     )   @allure.step(\"Check that {name} equals to {expected}\") def assert_equal(actual: Any, expected: Any, name: str):     \"\"\"     \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0434\u0432\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0432\u043d\u044b.      :param: actual (Any): \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.     :param: expected (Any): \u041e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.     :param: name (str): \u0418\u043c\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 (\u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f).     :raises: AssertionError: \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0435 \u0440\u0430\u0432\u043d\u044b.     \"\"\"     logger.info(f'Check that \"{name}\" equals to {expected}')      assert actual == expected, (         f'Incorrect value: \"{name}\". '         f'Expected value: {expected}. '         f'Actual value: {actual}'     ) <\/code><\/pre>\n<ul>\n<li>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <\/strong><code><strong>assert_status_code<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 HTTP-\u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u044b \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442 \u2014 \u0432\u044b\u0434\u0430\u0451\u0442 <code>AssertionError<\/code> \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <\/strong><code><strong>assert_equal<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0434\u0432\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0432\u043d\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 name \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043d\u0435\u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438 \u0432\u044b\u0434\u0430\u0451\u0442 <code>AssertionError<\/code> \u0441 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. \u041e\u043d\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e API \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.<\/p>\n<ol>\n<li>\n<p><code><strong>assert_create_operation<\/strong><\/code><strong> <\/strong>\u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e API \u0432\u0435\u0440\u043d\u0443\u043b\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><code><strong>assert_operation<\/strong><\/code><strong> <\/strong>\u2014<strong> <\/strong>\u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0435\u0451 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438.<\/p>\n<\/li>\n<\/ol>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/assertions\/operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/assertions\/operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">import allure  from schema.operations import CreateOperationSchema, OperationSchema, UpdateOperationSchema from tools.assertions.base import assert_equal from tools.logger import get_logger  logger = get_logger(\"OPERATIONS_ASSERTIONS\")   @allure.step(\"Check create operation\") def assert_create_operation(         actual: OperationSchema,         expected: CreateOperationSchema | UpdateOperationSchema ):     \"\"\"     \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0451\u043d\u043d\u044b\u0435 API \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f\/\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c.      :param: actual (OperationSchema): \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.     :param: expected (CreateOperationSchema | UpdateOperationSchema): \u041e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.     :raises: AssertionError: \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442.     \"\"\"     logger.info(\"Check create operation\")      assert_equal(actual.debit, expected.debit, \"debit\")     assert_equal(actual.credit, expected.credit, \"credit\")     assert_equal(actual.category, expected.category, \"category\")     assert_equal(actual.description, expected.description, \"description\")     assert_equal(actual.transaction_date, expected.transaction_date, \"transaction_date\")   @allure.step(\"Check operation\") def assert_operation(actual: OperationSchema, expected: OperationSchema):          logger.info(\"Check operation\")      assert_equal(actual.id, expected.id, \"id\")     assert_equal(actual.debit, expected.debit, \"debit\")     assert_equal(actual.credit, expected.credit, \"credit\")     assert_equal(actual.category, expected.category, \"category\")     assert_equal(actual.description, expected.description, \"description\")     assert_equal(actual.transaction_date, expected.transaction_date, \"transaction_date\") <\/code><\/pre>\n<ul>\n<li>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <\/strong><code><strong>assert_create_operation<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442 <code>debit<\/code>, <code>credit<\/code>, <code>category<\/code>, <code>description<\/code> \u0438 <code>transaction_date<\/code>.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <\/strong><code><strong>assert_operation<\/strong><\/code><\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0435\u0451 <code>id<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>API \u0442\u0435\u0441\u0442\u044b<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f:<\/p>\n<ul>\n<li>\n<p><strong>API \u043a\u043b\u0438\u0435\u043d\u0442\u044b<\/strong> \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u044b<\/strong> \u0434\u043b\u044f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/strong>, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u043d\u0435\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e JSON-\u0441\u0445\u0435\u043c\u044b<\/strong>, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043e\u0442\u0432\u0435\u0442\u044b \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0441\u0435\u0433\u043e \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tests\/test_operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tests\/test_operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from http import HTTPStatus  # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c enum HTTPStatus \u0432\u043c\u0435\u0441\u0442\u043e \u043c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0447\u0438\u0441\u0435\u043b  import allure import pytest  from clients.operations_client import OperationsClient from schema.operations import OperationsSchema, OperationSchema, CreateOperationSchema, UpdateOperationSchema from tools.assertions.base import assert_status_code from tools.assertions.operations import assert_operation, assert_create_operation from tools.assertions.schema import validate_json_schema   @pytest.mark.operations @pytest.mark.regression class TestOperations:     @allure.title(\"Get operations\")     def test_get_operations(self, operations_client: OperationsClient):         response = operations_client.get_operations_api()          assert_status_code(response.status_code, HTTPStatus.OK)         validate_json_schema(response.json(), OperationsSchema.model_json_schema())      @allure.title(\"Get operation\")     def test_get_operation(             self,             operations_client: OperationsClient,             function_operation: OperationSchema     ):         response = operations_client.get_operation_api(function_operation.id)         operation = OperationSchema.model_validate_json(response.text)          assert_status_code(response.status_code, HTTPStatus.OK)         assert_operation(operation, function_operation)          validate_json_schema(response.json(), operation.model_json_schema())      @allure.title(\"Create operation\")     def test_create_operation(self, operations_client: OperationsClient):         request = CreateOperationSchema()         response = operations_client.create_operation_api(request)         operation = OperationSchema.model_validate_json(response.text)          assert_status_code(response.status_code, HTTPStatus.CREATED)         assert_create_operation(operation, request)          validate_json_schema(response.json(), operation.model_json_schema())      @allure.title(\"Update operation\")     def test_update_operation(             self,             operations_client: OperationsClient,             function_operation: OperationSchema     ):         request = UpdateOperationSchema()         response = operations_client.update_operation_api(function_operation.id, request)         operation = OperationSchema.model_validate_json(response.text)          assert_status_code(response.status_code, HTTPStatus.OK)         assert_create_operation(operation, request)          validate_json_schema(response.json(), operation.model_json_schema())      @allure.title(\"Delete operation\")     def test_delete_operation(             self,             operations_client: OperationsClient,             function_operation: OperationSchema     ):         delete_response = operations_client.delete_operation_api(function_operation.id)         assert_status_code(delete_response.status_code, HTTPStatus.OK)          # \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430: \u0443\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0430         get_response = operations_client.get_operation_api(function_operation.id)         assert_status_code(get_response.status_code, HTTPStatus.NOT_FOUND) <\/code><\/pre>\n<ul>\n<li>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <\/strong><a href=\"https:\/\/docs.python.org\/3\/library\/http.html#http.HTTPStatus\" rel=\"noopener noreferrer nofollow\"><strong>HTTPStatus<\/strong><\/a><strong> \u0432\u043c\u0435\u0441\u0442\u043e <\/strong><a href=\"https:\/\/en.wikipedia.org\/wiki\/Magic_number_(programming)\" rel=\"noopener noreferrer nofollow\"><strong>\u043c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0447\u0438\u0441\u0435\u043b<\/strong><\/a><br \/>\u0412\u043c\u0435\u0441\u0442\u043e 200, 201, 404 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043a\u043e\u0434\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>HTTPStatus.OK<\/code>, <code>HTTPStatus.CREATED<\/code>, <code>HTTPStatus.NOT_FOUND<\/code>. \u042d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043a\u043e\u0434 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u043c \u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f<\/strong><br \/>\u041f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 GET <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\/1\" rel=\"noopener noreferrer nofollow\">\/fakebank\/accounts\/{id}<\/a> \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0443\u043b <strong>404 Not Found<\/strong>. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u0427\u0438\u0442\u0430\u0435\u043c\u044b\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 Allure-\u043e\u0442\u0447\u0435\u0442\u0435<\/strong><br \/>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/allurereport.org\/docs\/pytest\/#specify-description-links-and-other-metadata\" rel=\"noopener noreferrer nofollow\"><strong>@allure.title()<\/strong><\/a>, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0438\u043c\u0435\u043b\u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0432 Allure.<\/p>\n<\/li>\n<li>\n<p><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b pytest \u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430<\/strong><\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c pytest-\u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u0432 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/pytest.ini\" rel=\"noopener noreferrer nofollow\"><strong>pytest.ini<\/strong><\/a>, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435.<\/p>\n<pre><code>[pytest] addopts = -s -v python_files = *_tests.py test_*.py python_classes = Test* python_functions = test_* markers =     regression: \u041c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u0440\u0435\u0433\u0440\u0435\u0441\u0441\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.     operations:  \u041c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438.<\/code><\/pre>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432, \u0442\u0435\u0441\u0442\u044b \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438, \u0430 \u043d\u0435 \u043d\u0430 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u044f\u0445.<\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u043e\u043c\u043e\u0436\u0434\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0448\u0430\u0433\u0430\u043c\u0438 Allure, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439 JSON-\u0441\u0445\u0435\u043c \u0438 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u043c\u0438, \u043c\u044b \u0432\u044b\u043d\u043e\u0441\u0438\u043c \u044d\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0434\u0435\u043b\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u044b:<\/p>\n<ul>\n<li>\n<p><strong>\u0427\u0438\u0442\u0430\u0435\u043c\u044b\u043c\u0438<\/strong> \u2013 \u043a\u043e\u0434 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043b\u0430\u043a\u043e\u043d\u0438\u0447\u043d\u044b\u043c \u0438 \u0438\u043d\u0442\u0443\u0438\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0432 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438<\/strong> \u2013 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u0438\u043b\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u043c\u0438<\/strong> \u2013 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 API \u0438\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u0445 \u0432\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e, \u0430 \u043d\u0435 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0435\u0441\u0442\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0435, \u0430 \u0432\u0441\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b (\u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0448\u0430\u0433\u0438 Allure, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 API, \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0438 \u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f) \u0441\u043a\u0440\u044b\u0442\u044b \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u042d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435.<\/p>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a \u043d\u0430 CI\/CD<\/h2>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <a href=\"https:\/\/docs.github.com\/en\/actions\/writing-workflows\/quickstart\" rel=\"noopener noreferrer nofollow\">workflow-\u0444\u0430\u0439\u043b<\/a> \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 API-\u0442\u0435\u0441\u0442\u043e\u0432 \u0432 <a href=\"https:\/\/github.com\/features\/actions\" rel=\"noopener noreferrer nofollow\">GitHub Actions<\/a>, \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 <a href=\"https:\/\/allurereport.org\/\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0435\u0442\u0430<\/a> \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0435\u0433\u043e \u043d\u0430 <a href=\"https:\/\/pages.github.com\/\" rel=\"noopener noreferrer nofollow\">GitHub Pages<\/a>.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/.github\/workflows\/test.yml\" rel=\"noopener noreferrer nofollow\"><strong>\/.github\/workflows\/test.yml<\/strong><\/a><\/p>\n<pre><code class=\"yaml\">name: API tests  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 workflow  on:   push:     branches:       - main  # \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c workflow \u043f\u0440\u0438 \u043f\u0443\u0448\u0435 \u0432 main   pull_request:     branches:       - main  # \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c workflow \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 PR \u0432 main  jobs:   run-tests:  # \u0414\u0436\u043e\u0431\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432     runs-on: ubuntu-latest  # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e Ubuntu      steps:       - name: Check out repository  # \u041a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0432 \u0441\u0440\u0435\u0434\u0443 CI\/CD         uses: actions\/checkout@v4        - name: Set up Python  # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 Python         uses: actions\/setup-python@v5         with:           python-version: '3.12'  # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Python \u0432\u0435\u0440\u0441\u0438\u0438 3.12        - name: Install dependencies  # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430         run: |           python -m pip install --upgrade pip  # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c pip \u0434\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438           pip install -r requirements.txt  # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432 requirements.txt        - name: Run API tests with pytest and generate Allure results  # \u0417\u0430\u043f\u0443\u0441\u043a \u0442\u0435\u0441\u0442\u043e\u0432         run: |           pytest -m regression --alluredir=allure-results --numprocesses 2           # \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0442\u0435\u0441\u0442\u044b \u0441 \u043c\u0435\u0442\u043a\u043e\u0439 \"regression\"           # --alluredir=allure-results \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432 \u043f\u0430\u043f\u043a\u0443 allure-results           # --numprocesses 2 - \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0435\u0441\u0442\u044b \u0432 2 \u043f\u043e\u0442\u043e\u043a\u0430 (\u0443\u0441\u043a\u043e\u0440\u044f\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435)        - name: Upload Allure results  # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 GitHub Actions         if: always()  # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0443\u0441\u043f\u0435\u0445\u0430\/\u043d\u0435\u0443\u0441\u043f\u0435\u0445\u0430 \u0442\u0435\u0441\u0442\u043e\u0432         uses: actions\/upload-artifact@v4         with:           name: allure-results  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0430           path: allure-results  # \u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0430\u043c \u043e\u0442\u0447\u0435\u0442\u0430    publish-report:  # \u0414\u0436\u043e\u0431\u0430 \u0434\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 Allure-\u043e\u0442\u0447\u0435\u0442\u0430 \u043d\u0430 GitHub Pages     needs: [ run-tests ]  # \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f run-tests     runs-on: ubuntu-latest  # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e Ubuntu      steps:       - name: Check out repository  # \u041a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0432\u0435\u0442\u043a\u0443 gh-pages         uses: actions\/checkout@v4         with:           ref: gh-pages  # \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432 \u0432\u0435\u0442\u043a\u0435 gh-pages           path: gh-pages  # \u041a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0430\u043f\u043a\u0443 gh-pages        - name: Download Allure results  # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432         uses: actions\/download-artifact@v4         with:           name: allure-results  # \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0430           path: allure-results  # \u041f\u0443\u0442\u044c \u0434\u043b\u044f \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f        - name: Allure Report action from marketplace  # \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u0447\u0435\u0442\u0430 Allure         uses: simple-elf\/allure-report-action@v1.12         if: always()         with:           allure_results: allure-results  # \u041f\u0430\u043f\u043a\u0430 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432           allure_history: allure-history  # \u041f\u0430\u043f\u043a\u0430 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u043e\u0442\u0447\u0435\u0442\u043e\u0432        - name: Deploy report to Github Pages  # \u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043e\u0442\u0447\u0435\u0442\u0430 \u043d\u0430 GitHub Pages         if: always()         uses: peaceiris\/actions-gh-pages@v4         with:           github_token: ${{ secrets.GITHUB_TOKEN }}  # \u0422\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e           publish_branch: gh-pages  # \u041f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u043e\u0442\u0447\u0435\u0442 \u0432 \u0432\u0435\u0442\u043a\u0443 gh-pages           publish_dir: allure-history  # \u041f\u0430\u043f\u043a\u0430, \u0433\u0434\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442<\/code><\/pre>\n<p>\u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 actions \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0438\u0436\u0435:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/marketplace?type=actions\" rel=\"noopener noreferrer nofollow\">GitHub Actions Marketplace<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/actions\/setup-python\" rel=\"noopener noreferrer nofollow\">GitHub Action \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/actions\/checkout\" rel=\"noopener noreferrer nofollow\">GitHub Action \u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/actions\/upload-artifact\" rel=\"noopener noreferrer nofollow\">GitHub Action \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/actions\/download-artifact\" rel=\"noopener noreferrer nofollow\">GitHub Action \u0434\u043b\u044f \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/simple-elf\/allure-report-action\" rel=\"noopener noreferrer nofollow\">GitHub Actioin \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 Allure \u043e\u0442\u0447\u0435\u0442\u0430 \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0438\u0441\u0442\u043e\u0440\u0438\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/peaceiris\/actions-gh-pages\" rel=\"noopener noreferrer nofollow\">GitHubActions \u0434\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 GitHub Pages<\/a><\/p>\n<\/li>\n<\/ul>\n<h4>\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0434\u043b\u044f Workflow<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u043d\u0430 GitHub Actions \u0442\u043e, \u0431\u0443\u0434\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0430, \u0433\u043e\u0432\u043e\u0440\u044f\u0449\u0430\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0443 github token \u0438\u0437 workflow \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0435\u0442 \u043f\u0440\u0430\u0432 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0442\u044c \u0432 \u0440\u0435\u043f\u0437\u043e\u0438\u0442\u043e\u0440\u0438\u0439<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a8d\/c66\/fc8\/a8dc66fc85f106fd01ff3aeeb4cb0da7.png\" width=\"1892\" height=\"88\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a8d\/c66\/fc8\/a8dc66fc85f106fd01ff3aeeb4cb0da7.png\"\/><\/figure>\n<p>\u0414\u043b\u044f \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0430\u0432 workflow:<\/p>\n<ol>\n<li>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 <strong>Settings<\/strong> \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitHub.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8b8\/849\/b1e\/8b8849b1e200b49d2b77c7d219b5cc7d.png\" width=\"3024\" height=\"208\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8b8\/849\/b1e\/8b8849b1e200b49d2b77c7d219b5cc7d.png\"\/><\/figure>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b <strong>Actions<\/strong> \u2192 <strong>General<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3b1\/f24\/bda\/3b1f24bdadc770cf2c68428e9fb51255.png\" width=\"690\" height=\"208\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3b1\/f24\/bda\/3b1f24bdadc770cf2c68428e9fb51255.png\"\/><\/figure>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u043a\u0440\u0443\u0442\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u043d\u0438\u0437 \u0434\u043e \u0431\u043b\u043e\u043a\u0430 <strong>Workflow permissions<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u044e <strong>Read and write permissions<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 <strong>Save<\/strong> \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/42d\/393\/71d\/42d39371d59150fd28895bdc28dcb786.png\" width=\"1760\" height=\"702\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/42d\/393\/71d\/42d39371d59150fd28895bdc28dcb786.png\"\/><\/figure>\n<\/li>\n<\/ol>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u0448\u0430\u0433\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0434 \u0441 API-\u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439.<\/p>\n<h4>\u0417\u0430\u043f\u0443\u0441\u043a \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f Allure-\u043e\u0442\u0447\u0451\u0442\u0430<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\" rel=\"noopener noreferrer nofollow\"><strong>Actions<\/strong><\/a> \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\/runs\/14155381650\" rel=\"noopener noreferrer nofollow\"><strong>workflow<\/strong><\/a>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u044f\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u044b.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/670\/904\/e98\/670904e98d1061b5a1583732561a27c0.png\" width=\"3024\" height=\"1034\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/670\/904\/e98\/670904e98d1061b5a1583732561a27c0.png\"\/><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0439\u0434\u0443\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/8\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u0431\u0443\u0434\u0435\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d \u0432 \u0432\u0435\u0442\u043a\u0443 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/tree\/gh-pages\" rel=\"noopener noreferrer nofollow\">gh-pages<\/a>, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0441\u044f workflow <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\/runs\/14155385792\" rel=\"noopener noreferrer nofollow\"><strong>pages build and deployment<\/strong><\/a>. \u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/8\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u043d\u0430 GitHub Pages, \u0434\u0435\u043b\u0430\u044f \u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p><strong>\u0412\u0430\u0436\u043d\u043e!<\/strong> \u041f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c workflow \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0432\u0435\u0442\u043a\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/tree\/gh-pages\" rel=\"noopener noreferrer nofollow\">gh-pages<\/a>. \u0415\u0441\u043b\u0438 \u0432\u0435\u0442\u043a\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u0435\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u0438\u043d\u0430\u0447\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/8\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442\u0430<\/a> \u043d\u0430 GitHub Pages \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<h4>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a GitHub Pages<\/h4>\n<p>\u0415\u0441\u043b\u0438 workflow <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\/runs\/14155385792\" rel=\"noopener noreferrer nofollow\"><strong>pages build and deployment<\/strong><\/a> \u043d\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0441\u044f, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 GitHub Pages:<\/p>\n<ol>\n<li>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 <strong>Settings<\/strong> \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b <strong>Pages<\/strong> \u2192 <strong>Build and deployment<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435 \u043d\u0438\u0436\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430 \u044d\u0442\u043e\u0439 \u0436\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0432\u0438\u0434\u0436\u0435\u0442 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0439 Allure-\u043e\u0442\u0447\u0451\u0442.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8a4\/215\/a57\/8a4215a5781860fad8c93bda9755fa5b.png\" width=\"3022\" height=\"1180\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8a4\/215\/a57\/8a4215a5781860fad8c93bda9755fa5b.png\"\/><\/figure>\n<h4>\u0414\u043e\u0441\u0442\u0443\u043f \u043a Allure-\u043e\u0442\u0447\u0451\u0442\u0430\u043c<\/h4>\n<ul>\n<li>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043e\u0442\u0447\u0451\u0442 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442\u0441\u044f \u043d\u0430 GitHub Pages \u0441 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c workflow, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d \u0431\u044b\u043b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0435 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 Allure-\u043e\u0442\u0447\u0451\u0442\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u0432\u0435\u0442\u043a\u0435 <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/tree\/gh-pages\" rel=\"noopener noreferrer nofollow\">gh-pages<\/a>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u044f \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u043d\u0430 GitHub Pages, \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/8\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u0441 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2ca\/548\/eca\/2ca548eca7849b37b6e14ee74993f785.png\" width=\"3024\" height=\"838\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2ca\/548\/eca\/2ca548eca7849b37b6e14ee74993f785.png\"\/><\/figure>\n<p>\u041f\u043e \u0438\u0442\u043e\u0433\u0443, \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043d\u043e\u0432\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/8\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0441\u0442\u043e\u0440\u0438\u044e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u043f\u0440\u043e\u0433\u043e\u043d\u043e\u0432.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c67\/312\/7a8\/c673127a857e96eadaeb93b1a7f7e4b1.png\" width=\"3024\" height=\"1594\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c67\/312\/7a8\/c673127a857e96eadaeb93b1a7f7e4b1.png\"\/><\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412\u0441\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043a\u043e\u0434, \u043e\u0442\u0447\u0435\u0442\u044b \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 CI\/CD \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 \u043c\u043e\u0435\u043c <a href=\"https:\/\/github.com\/Nikita-Filonov\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u043e\u0435\u043a\u0442 \u0441 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/9\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0435\u0442 \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\" rel=\"noopener noreferrer nofollow\">CI\/CD \u0437\u0430\u043f\u0443\u0441\u043a\u0438 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/895452\/\"> https:\/\/habr.com\/ru\/articles\/895452\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Python, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 <strong>best practices.<\/strong> \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0438\u0445 \u0437\u0430\u043f\u0443\u0441\u043a \u0432 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/CI\/CD\" rel=\"noopener noreferrer nofollow\"><strong>CI\/CD<\/strong><\/a> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/actions\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Actions<\/strong><\/a> \u0438 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c <a href=\"https:\/\/nikita-filonov.github.io\/python-api-tests\/9\/index.html\" rel=\"noopener noreferrer nofollow\">Allure-\u043e\u0442\u0447\u0451\u0442<\/a> \u0441 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432. \u0426\u0435\u043b\u044c \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b, \u043d\u043e \u0438 \u043d\u0430\u0443\u0447\u0438\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0438\u0445 \u0432 CI\/CD, \u043f\u043e\u043b\u0443\u0447\u0430\u044f \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u043e\u0442\u0447\u0451\u0442\u044b \u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445.<\/p>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/features\/actions\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Actions<\/strong><\/a>, \u043d\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0438 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 CI\/CD-\u0441\u0438\u0441\u0442\u0435\u043c, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <a href=\"https:\/\/docs.gitlab.com\/ci\/\" rel=\"noopener noreferrer nofollow\">GitLab CI<\/a>, <a href=\"https:\/\/circleci.com\/\" rel=\"noopener noreferrer nofollow\">CircleCI<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/www.jenkins.io\/\" rel=\"noopener noreferrer nofollow\">Jenkins<\/a> \u2014 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441. \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 Allure-\u043e\u0442\u0447\u0451\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u043d\u0430 <a href=\"https:\/\/pages.github.com\/\" rel=\"noopener noreferrer nofollow\"><strong>GitHub Pages<\/strong><\/a>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432.<\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 API \u2014 <a href=\"https:\/\/en.wikipedia.org\/wiki\/REST\" rel=\"noopener noreferrer nofollow\"><strong>REST API<\/strong><\/a>, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c <a href=\"https:\/\/sampleapis.com\/api-list\/fakebank\" rel=\"noopener noreferrer nofollow\">FakeBank<\/a>. \u042d\u0442\u043e \u0443\u0447\u0435\u0431\u043d\u043e\u0435 API, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0435\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u043c\u0438 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438. \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/api.sampleapis.com\/fakebank\/accounts<\/strong><\/a>.<\/p>\n<h2>\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h2>\n<p>\u0412\u043e\u0442 \u0441\u0442\u0435\u043a \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/www.python.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Python 3.12<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>Pytest<\/strong><\/a> \u2014 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a REST API<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/pydantic_settings\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic Settings<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/faker.readthedocs.io\/en\/master\/\" rel=\"noopener noreferrer nofollow\"><strong>Faker<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/allurereport.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Allure<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0442\u0447\u0451\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/python-jsonschema.readthedocs.io\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>jsonschema<\/strong><\/a> \u2014 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c\u044b JSON-\u043e\u0442\u0432\u0435\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/pytest-dev\/pytest-xdist\" rel=\"noopener noreferrer nofollow\"><strong>pytest-xdist<\/strong><\/a> \u2014 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 HTTPX, \u0430 \u043d\u0435 Requests?<\/h4>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/requests.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Requests<\/strong><\/a> \u0445\u043e\u0442\u044c \u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0430, \u043d\u043e \u0443\u0436\u0435 \u0434\u0430\u0432\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c\u0441\u044f. \u0423 \u043d\u0435\u0451 \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 <a href=\"https:\/\/github.com\/psf\/requests\/issues\/3855#issuecomment-277931774\" rel=\"noopener noreferrer nofollow\"><strong>\u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0442\u0438\u043f\u043e\u0432<\/strong><\/a>, \u0445\u043e\u0442\u044f \u044d\u0442\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 Python \u0435\u0449\u0451 \u0432 \u0432\u0435\u0440\u0441\u0438\u0438 3.5 (\u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u0441\u043a\u043e\u0440\u043e \u0432\u044b\u0439\u0434\u0435\u0442 \u0443\u0436\u0435 3.14). \u041f\u0440\u043e\u0448\u043b\u043e <strong>\u0431\u043e\u043b\u0435\u0435 10 \u043b\u0435\u0442<\/strong>, \u043d\u043e \u0432 Requests \u0442\u0430\u043a \u0438 \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432, \u0447\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0441\u043b\u0430\u0431\u043e\u0439 \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<p><a href=\"https:\/\/requests.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Requests<\/strong><\/a> \u0432 \u0441\u0432\u043e\u0451 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0432\u043e\u0435\u0432\u0430\u043b\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f <strong>\u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0435<\/strong> \u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044e \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u0445 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432. \u041d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0435\u0441\u0442\u044c <strong>\u0431\u043e\u043b\u0435\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435, \u043c\u043e\u0449\u043d\u043e\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/strong> \u2014 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a>.<\/p>\n<p>\u0427\u0442\u043e \u0434\u0430\u0451\u0442 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 Requests:<\/p>\n<ul>\n<li>\n<p>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0442\u0438\u043f\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 <a href=\"https:\/\/www.python-httpx.org\/advanced\/clients\/\" rel=\"noopener noreferrer nofollow\"><strong>Client<\/strong><\/a> \u0434\u043b\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.python-httpx.org\/advanced\/event-hooks\/\" rel=\"noopener noreferrer nofollow\"><strong>Event hooks<\/strong><\/a> (\u0445\u0443\u043a\u0438 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439)<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 <strong>\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445<\/strong> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 <strong>HTTP\/2<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0441\u0451 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a> <strong>\u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c<\/strong>. Requests, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0442\u0430\u043a \u0438 \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0431\u0435\u0437 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u043f\u0438\u0448\u0435\u0442\u0435 <strong>legacy-\u043a\u043e\u0434<\/strong> \u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 <strong>\u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 API \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Python<\/strong>, \u0442\u043e <a href=\"https:\/\/www.python-httpx.org\/\" rel=\"noopener noreferrer nofollow\"><strong>HTTPX<\/strong><\/a><strong> \u2014 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440<\/strong>.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 Pydantic?<\/h4>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e: <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a><strong> \u2014 \u044d\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0434\u0435-\u0444\u0430\u043a\u0442\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 Python<\/strong>.<\/p>\n<p>\u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0412\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u043e\u0431\u043d\u043e <strong>\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c<\/strong> JSON<\/p>\n<\/li>\n<li>\n<p>\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u0433\u0443\u044e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 <a href=\"https:\/\/docs.pydantic.dev\/latest\/concepts\/pydantic_settings\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic Settings<\/strong><\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0423 <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u043f\u043e\u0447\u0442\u0438 \u043d\u0435\u0442 \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u0445 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432. <strong>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 <\/strong><a href=\"https:\/\/docs.python.org\/3\/library\/dataclasses.html\" rel=\"noopener noreferrer nofollow\"><strong>dataclasses<\/strong><\/a> \u0432 Python \u0434\u0430\u0436\u0435 \u0431\u043b\u0438\u0437\u043a\u043e \u043d\u0435 \u0434\u0430\u044e\u0442 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u044b <strong>\u0447\u0438\u0441\u0442\u044b\u0435, \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u0435 \u0438 \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435<\/strong> \u2014 <a href=\"https:\/\/docs.pydantic.dev\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Pydantic<\/strong><\/a> \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 pytest?<\/h4>\n<p>\u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e <a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a><strong> \u2014 \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0438\u0439 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f Python<\/strong>.<\/p>\n<ul>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c<\/strong>: \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u044b\u0435, \u0442\u0430\u043a \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b<\/p>\n<\/li>\n<li>\n<p><strong>\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c<\/strong>: \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u0444\u0438\u043a\u0441\u0442\u0443\u0440\u044b, \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0438, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Allure<\/p>\n<\/li>\n<li>\n<p><strong>\u0427\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c<\/strong>: \u0442\u0435\u0441\u0442\u044b \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0447\u0438\u0441\u0442\u044b\u043c\u0438 \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c<\/strong>: pytest \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 Python<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430 \u0444\u043e\u043d\u0435 \u044d\u0442\u043e\u0433\u043e <a href=\"https:\/\/behave.readthedocs.io\/en\/latest\/\" rel=\"noopener noreferrer nofollow\"><strong>Behave<\/strong><\/a><strong>, <\/strong><a href=\"https:\/\/robotframework.org\/\" rel=\"noopener noreferrer nofollow\"><strong>Robot Framework<\/strong><\/a> \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u044b\u043c\u0438 \u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u044e\u0449\u0438\u043c\u0438 \u0436\u0438\u0437\u043d\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<ul>\n<li>\n<p><strong>\u041b\u0438\u0448\u043d\u044f\u044f \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f<\/strong> \u2014 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043b\u0430\u0434\u043a\u0438<\/strong> \u2014 \u0442\u0435\u0441\u0442\u044b \u0432 \u0441\u0442\u0438\u043b\u0435 <a href=\"https:\/\/cucumber.io\/docs\/gherkin\/\" rel=\"noopener noreferrer nofollow\">Gherkin<\/a> \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u0440\u0430\u0441\u0438\u0432\u043e, \u043d\u043e \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043c\u0435\u0448\u0430\u044e\u0442 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438<\/p>\n<\/li>\n<li>\n<p> <strong>\u041c\u043d\u0438\u043c\u0430\u044f \u0432\u044b\u0433\u043e\u0434\u0430 \u0447\u0438\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/strong> \u2014 \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 <a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a> \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u0435\u0435, \u0447\u0435\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 <a href=\"https:\/\/cucumber.io\/docs\/gherkin\/\" rel=\"noopener noreferrer nofollow\">Gherkin<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 <strong>\u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u043c\u043e\u0449\u043d\u044b\u0435, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 API\/UI-\u0442\u0435\u0441\u0442\u044b \u2014 <\/strong><a href=\"https:\/\/docs.pytest.org\/en\/stable\/\" rel=\"noopener noreferrer nofollow\"><strong>pytest<\/strong><\/a><strong> \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0438\u0439 \u0432\u044b\u0431\u043e\u0440<\/strong>.<\/p>\n<h2>\u041c\u043e\u0434\u0435\u043b\u0438 \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 API <a href=\"https:\/\/api.sampleapis.com\/fakebank\/accounts\" rel=\"noopener noreferrer nofollow\"><strong>https:\/\/api.sampleapis.com\/fakebank\/accounts<\/strong><\/a>, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c <strong>\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>Pydantic<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <strong>\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/strong> JSON<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/networks\/#pydantic.networks.HttpUrl\" rel=\"noopener noreferrer nofollow\">HttpUrl<\/a> \u0438 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/networks\/#pydantic.networks.EmailStr\" rel=\"noopener noreferrer nofollow\">EmailStr<\/a>, \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 <strong>\u0443\u0434\u043e\u0431\u043d\u0443\u044e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e<\/strong><\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c:<\/p>\n<ol>\n<li>\n<p><code><strong>CreateOperationSchema<\/strong><\/code> \u2013 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p><code><strong>UpdateOperationSchema<\/strong><\/code> \u2013 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f <strong>PATCH<\/strong> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432)<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationSchema<\/strong><\/code> \u2013 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0441 id, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f <strong>\u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/strong><\/p>\n<\/li>\n<li>\n<p><code><strong>OperationsSchema<\/strong><\/code> \u2013 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"json\">{   \"id\": 25,   \"debit\": 6.99,   \"credit\": null,   \"category\": \"Merchandise\",   \"description\": \"Benderbr\u00e4u\",   \"transactionDate\": \"2016-02-25\" }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/schema\/operations.py\" rel=\"noopener noreferrer nofollow\"><strong>\/schema\/operations.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from datetime import date  from pydantic import BaseModel, Field, RootModel, ConfigDict   class CreateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.          \u041f\u043e\u043b\u044f:     - debit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u043e \u0441\u0447\u0451\u0442\u0430     - credit (float | None): \u0421\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0441\u0447\u0451\u0442     - category (str): \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - description (str): \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     - transaction_date (date): \u0414\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (\u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)  # \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c alias \u043f\u0440\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438      debit: float | None     credit: float | None     category: str     description: str     transaction_date: date = Field(alias=\"transactionDate\")  # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c alias \u0434\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f API   class UpdateOperationSchema(BaseModel):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 PATCH \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445).      \u0412\u0441\u0435 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445.      \u041f\u043e\u043b\u044f:     - debit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f     - credit (float | None): \u041d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f     - category (str | None): \u041d\u043e\u0432\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f     - description (str | None): \u041d\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435     - transaction_date (date | None): \u041d\u043e\u0432\u0430\u044f \u0434\u0430\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 (alias \"transactionDate\")     \"\"\"     model_config = ConfigDict(populate_by_name=True)      debit: float | None     credit: float | None     category: str | None      description: str | None     transaction_date: date | None = Field(alias=\"transactionDate\")   class OperationSchema(CreateOperationSchema):     \"\"\"     \u041c\u043e\u0434\u0435\u043b\u044c \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0430\u044f ID.          \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 CreateOperationSchema \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435:     - id (int): \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438     \"\"\"     id: int   class OperationsSchema(RootModel):     \"\"\"     \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.          \u041f\u043e\u043b\u0435:     - root (list[OperationSchema]): \u0421\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439     \"\"\"     root: list[OperationSchema] <\/code><\/pre>\n<ul>\n<li>\n<p><code><strong>CreateOperationSchema<\/strong><\/code> \u2013 <strong>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/strong>. \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0441\u0443\u043c\u043c\u044b \u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f (<code>debit<\/code>), \u0441\u0443\u043c\u043c\u044b \u0437\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f (<code>credit<\/code>), \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 (<code>category<\/code>), \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f (<code>description<\/code>) \u0438 \u0434\u0430\u0442\u044b (<code>transaction_date<\/code>).<\/p>\n<\/li>\n<li>\n<p><code><strong>UpdateOperationSchema<\/strong><\/code> \u2013 <strong>\u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f PATCH-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/strong>, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c <strong>\u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f<\/strong>. \u0412\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0434\u0435\u0441\u044c <strong>\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432\u0441\u0435\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationSchema<\/strong><\/code> \u2013 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f <code>CreateOperationSchema<\/code>, <strong>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0435 <\/strong><code><strong>id<\/strong><\/code>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u043e\u0442\u0432\u0435\u0442\u0430\u0445 API.<\/p>\n<\/li>\n<li>\n<p><code><strong>OperationsSchema<\/strong><\/code> \u2013 <strong>\u0441\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/strong>. \u041d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <a href=\"https:\/\/docs.pydantic.dev\/latest\/api\/root_model\/#RootModel\" rel=\"noopener noreferrer nofollow\">RootModel<\/a>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 Pydantic.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 Pydantic, \u0430 \u043d\u0435 TypedDict \u0438\u043b\u0438 dataclasses?<\/h4>\n<p>Pydantic \u2013 \u044d\u0442\u043e <strong>\u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/strong> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API-\u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/peps.python.org\/pep-0589\/\" rel=\"noopener noreferrer nofollow\"><strong>TypedDict<\/strong><\/a> \u0438 <a href=\"https:\/\/docs.python.org\/3\/library\/collections.html\" rel=\"noopener noreferrer nofollow\"><strong>NamedTuple<\/strong><\/a> \u2013 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0434\u043b\u044f <strong>\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432<\/strong>, \u043d\u043e \u043d\u0435 \u0434\u043b\u044f <strong>\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/strong>.<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.python.org\/3\/library\/dataclasses.html\" rel=\"noopener noreferrer nofollow\"><strong>dataclasses<\/strong><\/a> \u2013 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043e\u0439, \u043d\u043e \u0443 \u043d\u0438\u0445 <strong>\u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 JSON<\/strong>. \u041e\u043d\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0445\u043e\u0440\u043e\u0448\u043e \u0432 <strong>\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u044f\u0445<\/strong>, \u043d\u043e \u0434\u043b\u044f API <strong>Pydantic \u2013 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>Pydantic \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435<\/strong>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <strong>\u0430\u043b\u0438\u0430\u0441\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439<\/strong> \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 JSON <strong>\u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439<\/strong>.<\/p>\n<h2>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 API \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432. \u0427\u0442\u043e\u0431\u044b <strong>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/strong> \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0438 <strong>\u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f<\/strong>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <strong>\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <\/strong><a href=\"https:\/\/faker.readthedocs.io\/en\/master\/\" rel=\"noopener noreferrer nofollow\"><strong>Faker<\/strong><\/a> \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043a\u043b\u0430\u0441\u0441 <code><strong>Fake<\/strong><\/code>. \u041a\u043b\u0430\u0441\u0441 <code><strong>Fake<\/strong><\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c <strong>\u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441<\/strong> \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0443\u0436\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439.<\/p>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/python-api-tests\/blob\/main\/tools\/fakers.py\" rel=\"noopener noreferrer nofollow\"><strong>\/tools\/fakers.py<\/strong><\/a><\/p>\n<pre><code class=\"python\">from datetime import date, timedelta  from faker import Faker   class Fake:     \"\"\"     \u041a\u043b\u0430\u0441\u0441-\u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 Faker, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445     \u0434\u043b\u044f \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.         \"\"\"     def __init__(self, faker: Faker):         \"\"\"         \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 Fake \u0441 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c Faker.          :param faker: \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Faker \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.         \"\"\"         self.faker = faker      def date(self, start: timedelta = timedelta(days=-30), end: timedelta = timedelta()) -&gt; date:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0434\u0430\u0442\u0443 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.          :param start: \u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e -30 \u0434\u043d\u0435\u0439 \u043e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0430\u0442\u044b).         :param end: \u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u044f\u044f \u0434\u0430\u0442\u0430).         :return: \u0421\u043b\u0443\u0447\u0430\u0439\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.         \"\"\"         return self.faker.date_between(start_date=start, end_date=end)      def money(self, start: float = -100, end: float = 100) -&gt; float:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0443 \u0434\u0435\u043d\u0435\u0433.          :param start: \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e -100).         :param end: \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 100).         :return: \u0421\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0437\u0430\u043f\u044f\u0442\u043e\u0439 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435.         \"\"\"         return self.faker.pyfloat(min_value=start, max_value=end)      def category(self) -&gt; str:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432.          :return: \u041e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 ('food', 'taxi', 'fuel' \u0438 \u0442.\u0434.).         \"\"\"         return self.faker.random_element(['food', 'taxi', 'fuel', 'beauty', 'restaurants'])      def sentence(self) -&gt; str:         \"\"\"         \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-454201","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/454201","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=454201"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/454201\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=454201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=454201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=454201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}