{"id":370674,"date":"2024-05-21T04:30:41","date_gmt":"2024-05-21T04:30:41","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=370674"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-05-21T04:30:41","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=370674","title":{"rendered":"<span>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0435\u0437 \u043c\u043e\u043a\u043e\u0432: \u044f\u0437\u044b\u043a \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432. \u0427\u0430\u0441\u0442\u044c 2<\/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<p>\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/798771\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.<\/a><\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/h2>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u043c\u0438\u0440\u043e\u043c. \u0425\u043e\u0442\u044f \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043d\u0430 \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u043a\u043e\u0434\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u0435\u043d \u0438 \u0441 \u0442\u0440\u0443\u0434\u043e\u043c \u043f\u043e\u0434\u0434\u0430\u0451\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0438\u0437-\u0437\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u0420\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 (Infrastructure Wrappers)<\/p>\n<\/li>\n<li>\n<p><a href=\"#id2\">\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b (Narrow Integration Tests)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id3\">\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb (Paranoic Telemetry)<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"id1\" id=\"id1\"><\/a><\/p>\n<h3>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438<\/h3>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c, \u0441\u043b\u043e\u0436\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0447\u0430\u0441\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u0418\u0437\u043e\u043b\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043e. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u2014 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u2014 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043a\u043b\u0430\u0441\u0441-\u043e\u0431\u0451\u0440\u0442\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0447\u0451\u0442\u043a\u043e\u0435 \u0438 \u0447\u0438\u0441\u0442\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0435\u0441\u043f\u043e\u0440\u044f\u0434\u043e\u0447\u043d\u043e\u0433\u043e \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u043c\u0438\u0440\u0430 \u0432 \u0442\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<p>\u0418\u0437\u0431\u0435\u0433\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0412 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u043e\u0431\u0449\u0438\u0445 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>LoginClient<\/code> \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 <code>HttpClient<\/code>. \u0412 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043b\u0430\u0441\u0441 <code>DataStore<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <code>RelationalDb<\/code> \u0438 \u043a\u043b\u0430\u0441\u0441\u0430 <code>NoSqlDb<\/code>. \u0417\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043e\u0434\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0446\u0435\u043f\u043e\u0447\u0435\u043a \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 \u00ab\u043f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb. \u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0438\u0445 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"#id4\">\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 Nullability.<\/a><\/p>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u00ab<a href=\"https:\/\/martinfowler.com\/articles\/gateway-pattern.html\">\u0448\u043b\u044e\u0437\u0430\u043c\u0438<\/a>\u00bb \u0438\u043b\u0438 \u00ab\u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430\u043c\u0438\u00bb, \u0445\u043e\u0442\u044f \u044d\u0442\u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u044b \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0434\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a.<\/p>\n<p><a class=\"anchor\" name=\"id2\" id=\"id2\"><\/a><\/p>\n<h3>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0412 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0441\u0447\u0451\u0442\u0435, \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0441\u0435\u0442\u044c\u044e, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0438\u043b\u0438 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442 \u043a\u0430\u043a\u0443\u044e-\u043b\u0438\u0431\u043e \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0432\u044f\u0437\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c. \u0417\u0434\u0435\u0441\u044c \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e-\u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c\u0443. \u0414\u043b\u044f \u043a\u043e\u0434\u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0447\u0438\u0442\u0430\u0439\u0442\u0435 \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043e\u0431\u0440\u0430\u0449\u0430\u0439\u0442\u0435\u0441\u044c \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0442\u0443 \u0436\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u0447\u0442\u043e \u0438 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u0441\u0440\u0435\u0434\u0430. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u043e\u043b\u043a\u043d\u0451\u0442\u0441\u044f \u0441 \u0442\u043e\u043d\u043a\u0438\u043c\u0438 \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0439\u0442\u0435 \u0443\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435. \u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0430 \u0438\u0445 \u0437\u0430\u043f\u0443\u0441\u043a \u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u0443\u0434\u0443\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0438\u043b\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c \u0441\u0431\u043e\u0440\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0441\u0431\u043e\u044f\u043c\u0438 \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043b\u044e\u0434\u044c\u043c\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043e\u0431\u0449\u0443\u044e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0434\u043b\u044f \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438. \u0417\u0430\u0442\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0412\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u043d\u0435 \u043d\u0443\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435<a href=\"https:\/\/www.jamesshore.com\/v2\/projects\/nullables\/testing-without-mocks#fake-it\"> <\/a>\u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0443\u0442\u044c \u043a \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 <a href=\"#id11\">Fake It Once You Make It<\/a>, \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u0432 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0435.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 <code>LoginClient<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0433\u043e <code>HttpClient<\/code>. <code>LoginClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 Fake It Once You Make It, \u0430 <code>HttpClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of narrow integration tests for HttpClient (JavaScript + Node.js) import * as http from \"node:http\"; import HttpClient from \".\/http_client\";  const HOST = \"localhost\"; const PORT = 5001;  \/\/ Tests describe(\"HTTP Client\", () => {   let server;    before(async () => {     server = new TestServer();     await server.startAsync();   });    after(async () => {     await server.stopAsync();   });    beforeEach(function() {     server.reset();   });    it(\"performs request\", async () => {     await requestAsync({       host: HOST,       port: PORT,       method: \"POST\",       path: \"\/my\/path\",       headers: { myRequestHeader: \"myRequestValue\" },       body: \"my request body\"     });      assert.deepEqual(server.lastRequest, {       method: \"POST\",       path: \"\/my\/path\",       headers: { myrequestheader: \"myRequestValue\" },       body: \"my request body\"     });   });    it(\"returns response\", async () => {     server.setResponse({       status: 999,       headers: { myResponseHeader: \"myResponseValue\" },       body: \"my response\",     });      const response = await requestAsync();     assert.deepEqual(response, {       status: 999,       headers: { myresponseheader: \"myResponseValue\" },       body: \"my response\",     });   });    async function requestAsync(options = {     host: HOST,     port: PORT,     method: \"GET\",     path: \"\/irrelevant\/path\",   }) {     const client = HttpClient.create();     return client.requestAsync(options);   }  });  \/\/ Localhost HTTP server class TestServer {   constructor() {     this.reset();   }    reset() {     this._lastRequest = null;     this._nextResponse = {       status: 500,       headers: {},       body: \"response not specified\",     };   }    startAsync() {     return new Promise((resolve, reject) => {       this._server = http.createServer();       this._server.once(\"listening\", resolve);       this._server.once(\"error\", reject);       this._server.on(\"request\", this.#handleRequest.bind(this));       this._server.listen(PORT);     });   }    stopAsync() {     return new Promise((resolve, reject) => {       this._server.once(\"close\", resolve);       this._server.close();     });   }    setResponse(response) {     this._nextResponse = response;   }    get lastRequest() {     return this._lastRequest;   }    \/\/ In JavaScript, methods that start with \"#\" are private.   #handleRequest(request, response) {     let body = \"\";     request.on(\"data\", (chunk) => {       body += chunk;     });     request.on(\"end\", () => {       this.#storeRequest(request, body);       this.#sendResponse(response);     });   }    #storeRequest(request, body) {     const headers = { ...request.headers };     delete headers.connection;     delete headers[\"content-length\"];     delete headers.host;      this._lastRequest = {       method: request.method,       path: request.url,       headers,       body,     };   }    #sendResponse(response) {     response.statusCode = this._nextResponse.status;     Object.entries(this._nextResponse.headers).forEach(([key, value]) => {       response.setHeader(key, value);     });      response.end(this._nextResponse.body);   } } <\/code><\/pre>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u00ab\u043f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u043e\u0439 \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u0438\u00bb \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435.<\/p>\n<p><a class=\"anchor\" name=\"id3\" id=\"id3\"><\/a><\/p>\n<h3>\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb (Paranoic Telemetry)<\/h3>\n<p>\u0412\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u043d\u044b. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0432 \u0447\u0451\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c, \u2014 \u044d\u0442\u043e \u0438\u0445 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u043a\u0430\u0437. \u0424\u0430\u0439\u043b\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0442\u0435\u0440\u044f\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438. \u0421\u043b\u0443\u0436\u0431\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u043a\u043e\u0434\u044b \u043e\u0448\u0438\u0431\u043e\u043a, \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u0432\u043e\u0438 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0439\u0442\u0435, \u0447\u0442\u043e \u043e\u043d\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u044f\u0442 \u0432\u0430\u0441 \u0434\u043e\u0441\u0442\u0430\u0442\u044c, \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0439 \u043a\u043e\u0434 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0414\u043e\u043f\u0443\u0441\u043a\u0430\u0439\u0442\u0435, \u0447\u0442\u043e \u0440\u0430\u043d\u043e \u0438\u043b\u0438 \u043f\u043e\u0437\u0434\u043d\u043e \u0432\u0441\u0451 \u0441\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0441\u0431\u043e\u044f \u043b\u0438\u0431\u043e \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435, \u043b\u0438\u0431\u043e \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0434\u0430 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0448\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>\u0412\u0441\u0435 \u044d\u0442\u0438 \u0441\u043b\u0443\u0447\u0430\u0438 \u043e\u0442\u043a\u0430\u0437\u0430 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0437\u0430\u0442\u0440\u0430\u0442 \u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0438 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u0435. \u041f\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0430 \u043d\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b.<\/p>\n<p>\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0430 <a href=\"https:\/\/martinfowler.com\/bliki\/ContractTest.html\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/a>. \u042d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u0438\u0445 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a (\u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0432\u044b), \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0443\u043b\u043e\u0432\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0433\u043e\u043d\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p><a class=\"anchor\" name=\"id4\" id=\"id4\"><\/a><\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b Nullability<\/h2>\n<p>\u041e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434. \u042d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u043b\u044f \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a, \u043d\u043e \u0435\u0441\u043b\u0438 \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u2014 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u2014 \u0438\u043c\u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u0440\u0443\u0434\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c. \u00ab\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u00bb \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u043e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>Nullables<\/p>\n<\/li>\n<li>\n<p><a href=\"#id6\">\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 (Embedded Stub)<\/a> <\/p>\n<\/li>\n<li>\n<p><a href=\"#id7\">\u0422\u043e\u043d\u043a\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 (Thin Wrapper)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id8\">\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id9\">\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id10\">\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id11\">\u201cFake It Once You Make It\u201d <\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"id5\" id=\"id5\"><\/a><\/p>\n<h3>Nullables<\/h3>\n<p>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b \u0438 \u0441\u043b\u043e\u0436\u043d\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435. \u0425\u043e\u0442\u044f \u043e\u043d\u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a, \u043e\u043d\u0438 \u0438\u0437\u043b\u0438\u0448\u043d\u0438 \u0434\u043b\u044f \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u0438\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u041f\u043b\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u043e\u0434, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0438\u043c\u0435\u043b \u0444\u0430\u0431\u0440\u0438\u0447\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>createNull()<\/code>. \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 &#171;Nulled&#187;, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432\u0441\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u043e \u0432\u0435\u0434\u0451\u0442 \u0441\u0435\u0431\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u0432\u043e \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f\u0445 <em>(2)<\/em>. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043e\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.<\/p>\n<p><em>(2). Nullables \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0438 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c Null Object, \u043d\u043e \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u0438 \u0441\u0442\u0430\u043b\u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<\/em><\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u044b\u0437\u043e\u0432 <code>LoginClient.createNull().getUserInfo(...)<\/code><em> <\/em>\u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0435\u0437 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>Nullables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434 \u0438 \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0425\u043e\u0442\u044f Nulled \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445, \u043e\u043d\u0438 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u00ab\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u00bb \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Nullables \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u043f\u0446\u0438\u044e \u00ab\u0444\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u043e\u043d\u0430\u00bb \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of using Nullables to implement \"dry run\" option (JavaScript + Node.js) async initializeGitWriter(config) {   if (config.dryRun) {     return GitWriter.createNull();   }   else {     return GitWriter.create();   } }<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Nullables \u0432 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0434\u043b\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 URL-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ Example of using Nullables to implement cache warming (JavaScript + Node.js) async warmCacheAsync(popularUrls, log) {   for await (const url of popularUrls) {     await this.routeAsync(HttpRequest.createNull(url, log);   } }<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0441\u0442\u0430\u0431\u043e\u0432. \u0414\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d Fake It Once You Make It. \u0427\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 Nullable, \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 <a href=\"#id12\">\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043b\u0435\u0433\u0430\u0441\u0438-\u043a\u043e\u0434\u0430<\/a>.<\/p>\n<p>\u0415\u0441\u043b\u0438 Nullable \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0438\u043b\u0438 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 <a href=\"#id8\">\u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses)<\/a>. \u0415\u0441\u043b\u0438 \u043e\u043d \u0438\u043b\u0438 \u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d <a href=\"#id9\">\u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking)<\/a>. \u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u0440\u0435\u0430\u0433\u0438\u0440\u0443\u044e\u0442 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 <a href=\"#id10\">\u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation).<\/a><\/p>\n<p><a class=\"anchor\" name=\"id6\" id=\"id6\"><\/a><\/p>\n<h3>\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 (Embedded Stub)<\/h3>\n<p>\u0414\u043b\u044f Nullables \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e. \u041e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u2014 \u043e\u043a\u0440\u0443\u0436\u0438\u0442\u044c \u043b\u044e\u0431\u043e\u0439 \u043a\u043e\u0434, \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439\u0441\u044f \u043a \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u043c &#171;if&#187;, \u043d\u043e \u044d\u0442\u043e \u0440\u0435\u0446\u0435\u043f\u0442 \u0434\u043b\u044f \u0441\u043f\u0430\u0433\u0435\u0442\u0442\u0438-\u043a\u043e\u0434\u0430. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0414\u0435\u043b\u0430\u044f \u043a\u043e\u0434 Nullable, \u043d\u0435 \u043c\u0435\u043d\u044f\u0439\u0442\u0435 \u0441\u0432\u043e\u0439 \u043a\u043e\u0434. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0434\u043b\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c.<\/p>\n<p>\u0412 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u043c\u0438\u043d\u0438\u043c\u0443\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043e\u0434\u0430. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043b\u0438\u0448\u043d\u0435\u0433\u043e \u2014 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0447\u0435\u0440\u0435\u0437 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u043e\u0434\u0430. \u041f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u0435 \u0435\u0451 \u0432 \u0442\u043e\u0442 \u0436\u0435 \u0444\u0430\u0439\u043b, \u0447\u0442\u043e \u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u043a\u043e\u0434, \u0447\u0442\u043e\u0431\u044b \u0435\u0451 \u0431\u044b\u043b\u043e \u043b\u0435\u0433\u043a\u043e \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u043e\u0434\u0430.<\/p>\n<p>\u041f\u0438\u0448\u0438\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u043a\u043e\u0434\u0430, \u0430 \u043d\u0435 \u0441\u0432\u043e\u0435\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u043b\u0438, \u043a\u0430\u043a \u043a\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435. \u0421\u043b\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u0442\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u043a\u043e\u0434\u0430. \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0443\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u044e\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0443\u0434\u0435\u043b\u044f\u044f \u043e\u0441\u043e\u0431\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u043c \u0441\u043b\u0443\u0447\u0430\u044f\u043c, \u0442\u0430\u043a\u0438\u043c \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434. \u0417\u0430\u0442\u0435\u043c \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 Nulled, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043f\u0440\u043e\u0439\u0434\u0443\u0442, \u0435\u0441\u043b\u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0442\u0430\u043a\u043e\u0433\u043e \u0436\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412\u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 JS-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>Math<\/code>:<\/p>\n<pre><code class=\"javascript\">\/\/ An Infrastructure Wrapper for a random die roller. (JavaScript)  \/\/ Infrastructure Wrapper export default class DieRoller {    \/\/ Normal factory   static create() {     return new DieRoller(Math);    \/\/ \"Math\" is a built-in JavaScript global   }    \/\/ Null factory   static createNull() {     return new DieRoller(new StubbedMath());   }    \/\/ Shared initialization code   constructor(math) {     this._math = math;   }    \/\/ Infrastructure wrapper implementation.   \/\/ This is the same code you would write without a stub.   roll(amount) {     const randomNumber = this._math.random();     return Math.trunc((randomNumber * 6) + 1);    \/\/ There's no need to stub Math.trunc, so we use the real Math library here   } };  \/\/ Embedded Stub. Note that we only stub the function we use. class StubbedMath {   random() {     return 0;   } }<\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440. \u0412 \u043d\u0451\u043c \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 http-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Node.js:<\/p>\n<pre><code class=\"javascript\">\/\/ An infrastructure wrapper for a generic HTTP client. (JavaScript + Node.js) import * as http from \"node:http\"; import { EventEmitter } from \"node:events\";  export default class HttpClient {    \/\/ Normal factory   static create() {     return new HttpClient(http);   }    \/\/ Null factory   static createNull() {     return new HttpClient(new StubbedHttp());   }    \/\/ Shared initialization code   constructor(http) {     this._http = http;   }    \/\/ Infrastructure wrapper implementation.   \/\/ This is the same code you would write without a stub.   async requestAsync({ host, port, method, path, headers = {}, body = \"\" }) {     if (method === \"GET\" &amp;&amp; body !== \"\") throw new Error(\"Don't include body with GET requests; Node won't send it\");      const httpOptions = { host, port, method, path, headers };     const request = this.#sendRequest(httpOptions, body);      await new Promise((resolve, reject) => {       this.#handleResponse(request, resolve);       this.#handleError(request, reject);     });   }    \/\/ In JavaScript, methods that start with \"#\" are private.   #sendRequest(httpOptions, body) {     const request = this._http.request(httpOptions);     request.end(body);     return request;   }    #handleResponse(request, resolve) {     request.once(\"response\", (response) => {       let body = \"\";       response.on(\"data\", (chunk) => {         body += chunk;       });       response.on(\"end\", () => {         resolve({           status: response.statusCode,           headers,           body,         });       });     });   }    #handleError(request, reject) {     request.once(\"error\", reject);   } };  \/\/ Embedded Stub. Note that it\u2019s built exactly for the needs of the infrastructure code, nothing more. class StubbedHttp {   request() {     return new StubbedRequest();   } }  class StubbedRequest extends EventEmitter {   end() {     \/\/ setImmediate() is used to make the emit() call asynchronous, duplicating the behavior of real code     setImmediate(() => this.emit(\"response\", new StubbedResponse()));   } }  class StubbedResponse extends EventEmitter {   constructor() {     super();     setImmediate(() => {       this.emit(\"data\", \"Nulled HttpClient response\");       this.emit(\"end\");     });   }   get statusCode() {     return 200;   }   get headers() {     return {};   } }<\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432. \u0415\u0441\u043b\u0438 \u0432\u0430\u0448 \u044f\u0437\u044b\u043a \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u044d\u0442\u043e\u0433\u043e, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, Java \u0438\u043b\u0438 C#, \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0442\u043e\u043d\u043a\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 (\u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0438\u0436\u0435).<\/p>\n<p>Nullables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434, \u0438, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u044c, \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u2014 \u0442\u043e\u0436\u0435. \u041e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0438\u0434\u0435\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434\u0435, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041e\u0434\u043d\u0430\u043a\u043e \u044d\u0442\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u0438 \u043b\u0438\u0448\u0438\u0442 \u0432\u0430\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Nulled \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u043e.<\/p>\n<p><a class=\"anchor\" name=\"id7\" id=\"id7\"><\/a><\/p>\n<h3>\u0422\u043e\u043d\u043a\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430<\/h3>\n<p>\u0422\u0430\u043a\u0438\u0435 \u044f\u0437\u044b\u043a\u0438, \u043a\u0430\u043a Java \u0438 C#, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0438\u043c\u0435\u043b\u0430 \u043e\u0431\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c\u044e. \u0417\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043d\u0435\u0442. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0412 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0439\u0442\u0435 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u2014 \u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0439\u0442\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434\u0435. \u041f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0434\u0432\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u044b\u0437\u043e\u0432\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0438 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<pre><code class=\"java\">\/\/ A simple Infrastructure Wrapper for a random die roller. (Java) \/\/ It has an embedded stub for Java's standard \u201cRandom\u201d library. \/\/ Based on an example created with Ted M. Young in his Yacht codebase. public class DieRoller {   private final RandomWrapper random;    \/\/ Normal factory   public static DieRoller create() {     return new DieRoller(new RealRandom());   }    \/\/ Null factory   public static DieRoller createNull() {     return new DieRoller(new StubbedRandom());   }    \/\/ Private constructor with shared initialization code   private DieRoller(RandomWrapper random) {     this.random = random;   }    \/\/ Infrastructure wrapper implementation.   \/\/ This is the same code you would write without a stub.   public int roll() {     return random.nextInt(6) + 1;   }    \/\/ Interface for Thin Wrapper. Note that we match the real code's interface exactly,   \/\/ and we only include the function we use.   private interface RandomWrapper {     int nextInt(int bound);   }    \/\/ Real implementation of Thin Wrapper   private static class RealRandom implements RandomWrapper {     private final Random random = new Random();      @Override     public int nextInt(int bound) {       return this.random.nextInt(bound);     }   }    \/\/ Embedded Stub implementation of Thin Wrapper   private static class StubbedRandom implements RandomWrapper {     @Override     public int nextInt(int bound) {       return 0;     }   } }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0439 \u043a\u043e\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0442\u0438\u043f\u044b, \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0438 \u044d\u0442\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u0442\u0438\u043f\u044b. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0439\u0442\u0435 \u0442\u043e\u0447\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430\u043c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<pre><code class=\"java\">\/\/ Infrastructure Wrapper for an HTTP request. (Java + Spring Boot's RestTemplate) \/\/ Based on an example created with Ted M. Young in his Yacht codebase. public class AverageScoreFetcher {   private static final String YACHT_AVERAGE_API_URI = \"http:\/\/localhost:8080\/api\/averages?scoreCategory={scoreCategory}\";    private final RestTemplateWrapper restTemplate;    \/\/ Normal factory   public static AverageScoreFetcher create() {     return new AverageScoreFetcher(new RealRestTemplate());   }    \/\/ Null factory   public static AverageScoreFetcher createNull() {     return new AverageScoreFetcher(new StubbedRestTemplate());   }    \/\/ Private constructor with shared initialization code   private AverageScoreFetcher(RestTemplateWrapper restTemplate) {     this.restTemplate = restTemplate;   }    \/\/ Infrastructure wrapper implementation   public double averageFor(ScoreCategory scoreCategory) {     ResponseEntityWrapper&lt;CategoryAverage> entity = restTemplate.getForEntity(       YACHT_AVERAGE_API_URI,       CategoryAverage.class,       scoreCategory.toString()     );     return entity.getBody().getAverage();   }    \/\/ Interfaces for Thin Wrapper. Note that we only include the functions we use.   interface RestTemplateWrapper {     &lt;T> ResponseEntityWrapper&lt;T> getForEntity(String url, Class&lt;T> responseType, Object... uriVariables);   }    interface ResponseEntityWrapper&lt;T> {     T getBody();   }    \/\/ Real implementations of Thin Wrapper   private static class RealRestTemplate implements RestTemplateWrapper {     private final RestTemplate restTemplate = new RestTemplate();      public &lt;T> ResponseEntityWrapper&lt;T> getForEntity(String url, Class&lt;T> responseType, Object... uriVariables) {       return new RealResponseEntity&lt;T>(restTemplate.getForEntity(url, responseType, uriVariables));     }   }    private static class RealResponseEntity&lt;T> implements ResponseEntityWrapper&lt;T> {     private ResponseEntity&lt;T> entity;      RealResponseEntity(ResponseEntity&lt;T> entity) {       this.entity = entity;     }      public T getBody() {       return this.entity.getBody();     }   }    \/\/ Stubbed implementations of Thin Wrapper   private static class StubbedRestTemplate implements RestTemplateWrapper {     @Override     public &lt;T> ResponseEntityWrapper&lt;T> getForEntity(String url, Class&lt;T> responseType, Object... uriVariables) {       return new StubbedResponseEntity&lt;>();     }   }    private static class StubbedResponseEntity&lt;T> implements ResponseEntityWrapper&lt;T> {     @Override     public T getBody() {       return (T) new CategoryAverage(\"Nulled AverageScoreFetcher category\", 42.0);     }   }  }<\/code><\/pre>\n<p><a class=\"anchor\" name=\"id8\" id=\"id8\"><\/a><\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b<\/h3>\n<p>\u0422\u0435\u0441\u0442\u044b \u043a\u043e\u0434\u0430 \u0441 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0438 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0439. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b Nullable \u0438 \u0437\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u0443\u0439\u0442\u0435 \u0444\u0430\u0431\u0440\u0438\u043a\u0443 <code>createNull()<\/code><em> <\/em>\u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u043b\u0430 \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0439\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0438\u043c\u043e\u0433\u043e \u0438\u0437\u0432\u043d\u0435 <em>\u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f<\/em> \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0430 \u043d\u0435 \u0435\u0451 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c Nullable \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0438\u043f\u043e\u0432 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432, \u0437\u0430\u0434\u0430\u0439\u0442\u0435 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0438\u0437 \u043d\u0438\u0445 \u0441\u0432\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442. \u0415\u0441\u043b\u0438 \u0432\u0430\u0448 \u044f\u0437\u044b\u043a \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 <code>Options<\/code>, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0432 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0435 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/798771\/#id13\">\u042d\u043a\u0440\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440 (Signature Shielding)<\/a>.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f <code>LoginController<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 Nullable <code>LoginClient<\/code>. \u0425\u043e\u0442\u044f <code>LoginClient<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 HTTP. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0430\u0434\u0440\u0435\u0441\u0435 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u043e\u0448\u0435\u0434\u0448\u0435\u0433\u043e \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0447\u0442\u043e \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0431\u043e\u0442\u0438\u0442\u0441\u044f <code>LoginController<\/code> \u0438 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u044b.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of configuring multiple types of responses. (JavaScript) it(\"logs successful login\", async () => {   \/\/ Configure login client dependency   const loginClient = LoginClient.createNull(     email: \"my_authenticated_email\",  \/\/ configure email address     emailVerified: true,              \/\/ configure whether email is verified   );    \/\/ Run production code   const { logOutput } = await performLogin({ loginClient }));  \/\/ Signature Shielding    \/\/ Check results   assert.deepEqual(logOutput.data, [ \"Login: my_authenticated_email (verified)\" ]);   \/\/ Output Tracking });<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u0448 \u043a\u043b\u0430\u0441\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0432 \u0432\u0438\u0434\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0438\u043b\u0438 \u0441\u043f\u0438\u0441\u043a\u0430. \u0427\u0430\u0441\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0434\u0432\u0430 \u0442\u0438\u043f\u0430 \u0434\u0430\u043d\u043d\u044b\u0445: \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0434\u0430\u0451\u0442 \u0440\u0430\u0437\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f; \u0438 \u043e\u0434\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u043e\u0442\u0432\u0435\u0442 \u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 Nullable <code>DieRoller<\/code> \u0441 \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0445 \u0431\u0440\u043e\u0441\u043a\u043e\u0432 \u043a\u0443\u0431\u0438\u043a\u0430:<\/p>\n<pre><code class=\"javascript\">\/\/ Example of a single type of response with multiple return values. (JavaScript) \/\/ Inspired by an example created with Ted M. Young in his Yacht codebase. it(\"rolls a hand of dice\", async () => {   \/\/ Configure die rolls   const dieRoller = DieRoller.createNull([ 1, 2, 3, 4, 5 ]);    \/\/ Run production code   const game = new Game(dieRoller);   const hand = game.roll();    \/\/ Check results   assert.deepEqual(hand, HandOfDice.create(1, 2, 3, 4, 5)); });<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 Nullable \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0432 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0435. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d Fake It Once You Make It. \u0412 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c.<\/p>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u0431 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0433\u043e \u0431\u0440\u043e\u0441\u043a\u0430 \u043a\u0443\u0431\u0438\u043a\u0430. \u041e\u043d \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u043d\u0430 \u0442\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439: \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u0440\u043e\u0441\u043a\u0430 \u043a\u0443\u0431\u0438\u043a\u043e\u0432. \u0412\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0435 \u044d\u0442\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0434\u043e \u0443\u0440\u043e\u0432\u043d\u044f, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c DieRoller \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0435 \u0447\u0438\u0441\u043b\u0430 \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u043e\u0442 \u043d\u0443\u043b\u044f \u0434\u043e \u0435\u0434\u0438\u043d\u0438\u0446\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0431\u0440\u043e\u0441\u043e\u043a 6 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u0447\u0438\u0441\u043b\u043e \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 0.83333.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of implementing Configurable Responses in an Embedded Stub. (JavaScript)  \/\/ Infrastructure Wrapper export default class DieRoller {    static create() {     return new DieRoller(Math);    \/\/ \"Math\" is a built-in JavaScript global   }    \/\/ Null factory with Configurable Responses   \/\/ If a number is provided, it always returns that number.   \/\/ If an array is provided, it returns exactly the numbers provided, then throws an error when it runs out.   \/\/ If nothing is provided, it defaults to returning ones.   static createNull(rolls = 1) {                    \/\/ set default to 1     return new DieRoller(new StubbedMath(rolls));   \/\/ pass configuration to Embedded Stub   }    constructor(math) {     this._math = math;   }    roll(amount) {     const randomNumber = this._math.random();     return Math.trunc((randomNumber * 6) + 1);      \/\/ There's no need to stub Math.trunc, so we use the real Math global   } };  \/\/ Embedded Stub with Configurable Responses class StubbedMath {    constructor(rolls) {     \/\/ Store configured responses     this._rolls = rolls;   }    random() {     \/\/ Use configured responses     const roll = this.#nextRoll();    \/\/ Get configuration to use     return (roll - 1) \/ 6;            \/\/ Convert to float to match behavior of real Math.random()   }    \/\/ Retrieve configured response   #nextRoll() {     if (Array.isArray(this._rolls)) {       \/\/ Configuration is an array, so return the next roll in the array       const roll = this._rolls.shift();       if (roll === undefined) throw new Error(\"No more rolls configured in nulled DieRoller\");       return roll;     }     else {       \/\/ Configuration is a number, so always return that number       return this._rolls;     }   } }<\/code><\/pre>\n<p>\u0412\u044b\u0448\u0435\u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043f\u0443\u0442\u0451\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f <code>#nextRoll()<\/code> \u0432 \u043e\u0431\u0449\u0438\u0439 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">\/\/ Example of implementing an embedded stub with a ConfigurableResponses helper class (JavaScript) class StubbedMath {   constructor(rolls) {     this._rolls = ConfigurableResponses.create(rolls);   }    random() {     return (this._rolls.next() - 1) \/ 6;   } }<\/code><\/pre>\n<p>\u042d\u0442\u043e JavaScript-\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f <code>ConfigurableResponses<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0441\u0432\u043e\u0451\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0435:<\/p>\n<pre><code class=\"javascript\">\/\/ Copyright 2023 Titanium I.T. LLC. MIT License. export default class ConfigurableResponses {    \/\/ Create a list of responses (by providing an array),   \/\/ or a single repeating response (by providing any other type).   \/\/ 'Name' is optional and used in error messages.   static create(responses, name) {     return new ConfigurableResponses(responses, name);   }    \/\/ Convert all properties in an object into ConfigurableResponse instances.   \/\/ For example, { a: 1 } becomes { a: ConfigurableResponses.create(1) }.   \/\/ 'Name' is optional and used in error messages.   static mapObject(responseObject, name) {     const entries = Object.entries(responseObject);     const translatedEntries = entries.map(([ key, value ]) => {       const translatedName = name === undefined ? undefined : `${name}: ${key}`;       return [ key, ConfigurableResponses.create(value, translatedName )];     });     return Object.fromEntries(translatedEntries);   }    constructor(responses, name) {     this._description = name === undefined ? \"\" : ` in ${name}` ;     this._responses = Array.isArray(responses)       ? [ ...responses ]       : responses;   }    \/\/ Get next configured response. Throws an error when configured with a list   \/\/ of responses and no more responses remain.   next() {     const response = Array.isArray(this._responses)       ? this._responses.shift()       : this._responses;     if (response === undefined) throw new Error(`No more responses configured${this._description}`);      return response;   }  };<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"#id9\">\u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking)<\/a>. \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u0440\u0435\u0430\u0433\u0438\u0440\u0443\u044e\u0449\u0438\u043c\u0438 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"#id10\">\u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation).<\/a><\/p>\n<p><a class=\"anchor\" name=\"id9\" id=\"id9\"><\/a><\/p>\n<h3>\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking)<\/h3>\n<p>\u0422\u0435\u0441\u0442\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0434\u043b\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0438\u0448\u0443\u0442 \u0432\u043e \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0431\u044b\u043b\u0430 \u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c. \u041d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0430 \u0438 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0417\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u0430\u0436\u0434\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c <code>trackXxx()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u0432\u0438\u0434\u0438\u043c\u044b\u0435 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0437\u0430\u043f\u0438\u0441\u0438. \u041f\u0443\u0441\u0442\u044c \u043e\u043d \u0434\u0435\u043b\u0430\u0435\u0442 \u044d\u0442\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0442\u043e\u0433\u043e, \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<p>\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u0445 <em>\u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f<\/em>, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0431\u043e\u0442\u044f\u0442\u0441\u044f \u0432\u0430\u0448\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0430 \u043d\u0435 \u0432 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 <em>\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/em> \u043a\u043e\u0434\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043b\u043e\u0433\u0433\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 <code>stdout<\/code>, \u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u043c \u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u0430\u0436\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. Output Tracking \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u043d\u0435 \u0441\u0442\u0440\u043e\u043a\u0443.<\/p>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>trackXxx()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u043b\u0430 <code>OutputTracker<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0441\u043b\u0443\u0448\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434\u043e\u043c. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 <code>OutputTracker<\/code> \u043d\u0430 JavaScript \u0438 Java, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0441\u0432\u043e\u0451\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0435. \u041e\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u0435\u0441\u0442\u0430 <code>LoginPage<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0438\u0448\u0435\u0442 \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 <code>Log<\/code>, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of using Output Tracking (JavaScript)  \/\/ Application layer test it(\"writes to log when user logs in\", async () => {   \/\/ Set up a log and track its output   const log = Log.createNull();   const logOutput = log.trackOutput();    \/\/ Instantiate the code under test   const loginPage = new LoginPage(log);    \/\/ Run the code   const formData = \/\/ code to set up \"my_email\" login here   await loginPage.postAsync(formData);    \/\/ Check the log output   assert.deepEqual(logOutput.data, [{     alert: \"info\",     message: \"User login\",     email: \"my_email\",   }]); });  \/\/ Application layer code class LoginPage {   constructor(log) {     this._log = log;   }    async postAsync(formData) {     const email = \/\/ code to parse formData and verify login goes here      \/\/ Code under test     this.log.info({       message: \"User login\",       email,     });   } }  \/\/ High-level \"Log\" infrastructure wrapper used by the code under test  import Clock from \"clock\";                        \/\/ Low-level infrastructure wrapper import Stdout from \"stdout\";                      \/\/ Low-level infrastructure wrapper import { EventEmitter } from \"node:events\";       \/\/ Standard Node.js event library import OutputTracker from \"output_tracker\";       \/\/ Output tracking library  const OUTPUT_EVENT = \"output\";                    \/\/ Event to emit when output occurs  class Log {   static create() {     return new Log(Clock.create(), Stdout.create());   }    static createNull({     clock = Clock.createNull(),                   \/\/ Fake It Once You Make It     stdout = Stdout.createNull(),   } = {}) {     return new Log(clock, stdout);   }    constructor(clock, stdout) {     this._clock = clock;     this._stdout = stdout;      this._emitter = new EventEmitter();           \/\/ Event emitter used when output occurs   }    \/\/ Output tracker   trackOutput() {     return OutputTracker.create(this._emitter, OUTPUT_EVENT);   }    \/\/ The method called by the code under test   info(data) {     data.alert = \"info\";      \/\/ Write the log     const now = this._clock.formattedTimestamp();     const dataJson = JSON.stringify(data);     this._stdout.write(`${now} ${dataJson}`);      \/\/ Emit the event. This is received by the OutputTracker.     this._emitter.emit(OUTPUT_EVENT, data);   } }<\/code><\/pre>\n<p>\u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, Output Tracking \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u0441\u0430\u043c\u044b\u043c, \u0447\u0442\u043e \u0438 \u0448\u043f\u0438\u043e\u043d (\u0442\u0438\u043f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u0430), \u043d\u043e \u0435\u0441\u0442\u044c \u0432\u0430\u0436\u043d\u043e\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u0435. Output Tracking \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 <em>\u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/em>, \u0430 \u0448\u043f\u0438\u043e\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 <em>\u0432\u044b\u0437\u043e\u0432\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/em>. \u0412 Output Trackers \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u0430 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u044b\u043b\u0430 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0434\u043b\u044f \u0435\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u0440\u0438 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0435 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043c\u0435\u043d\u044f\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u044f Output Trackers \u0438 \u0437\u0430\u0432\u0438\u0441\u044f\u0449\u0438\u0435 \u043e\u0442 \u043d\u0438\u0445 \u0442\u0435\u0441\u0442\u044b.<\/p>\n<p>\u042d\u0442\u043e JavaScript-\u0432\u0435\u0440\u0441\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u0430 <code>OutputTracker<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445:<\/p>\n<pre><code class=\"javascript\">\/\/ Copyright 2020-2022 Titanium I.T. LLC. MIT License. export default class OutputTracker {    static create(emitter, event) {     return new OutputTracker(emitter, event);   }    constructor(emitter, event) {     this._emitter = emitter;     this._event = event;     this._data = [];      this._trackerFn = (text) => this._data.push(text);     this._emitter.on(this._event, this._trackerFn);   }    get data() {     return this._data;   }    clear() {     const result = [ ...this._data ];     this._data.length = 0;     return result;   }    stop() {     this._emitter.off(this._event, this._trackerFn);   }  }<\/code><\/pre>\n<p>\u041d\u0438\u0436\u0435 \u044f \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0438\u043b Java-\u0432\u0435\u0440\u0441\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>OutputTracker<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0441\u043e\u0437\u0434\u0430\u043b \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0422\u0435\u0434\u043e\u043c \u041c. \u042f\u043d\u0433\u043e\u043c. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 Java \u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u044d\u043c\u0438\u0442\u0442\u0435\u0440\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043e\u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e-\u0434\u0440\u0443\u0433\u043e\u043c\u0443. \u0412\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440 \u0435\u0451 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0435\u0440\u0442\u043a\u0435 <code>Log<\/code> \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430:\u00a0<\/p>\n<pre><code class=\"java\">\/\/ Example of Output Tracking in Java public class Log {   \/\/ Instantiate the event emitter   private final OutputListener&lt;LogData> outputListener = new OutputListener&lt;>();    public static Log create...   public static Log createNull...   private Log...    \/\/ Create the output tracker   public OutputTracker&lt;LogData> trackOutput() {     return outputListener.createTracker();   }    public void info(Map&lt;String, LogData> data) {     \/\/ ...      \/\/ Emit the event     outputListener.track(data);   } }<\/code><\/pre>\n<p>\u042d\u0442\u043e Java-\u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b <code>OutputTracker<\/code>. \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435 \u0435\u0451 \u043d\u0430 \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430.<\/p>\n<pre><code class=\"java\">---- OutputListener.java ---- \/\/ Copyright 2022 Titanium I.T. LLC and Ted M. Young. MIT License. package com.jamesshore.output_tracker;  import java.util.ArrayList; import java.util.List;  public class OutputListener&lt;T> {   private final List&lt;OutputTracker&lt;T>> listeners = new ArrayList&lt;>();    public void track(T data) {     listeners.forEach(tracker -> tracker.add(data));   }    public OutputTracker&lt;T> createTracker() {     OutputTracker&lt;T> tracker = new OutputTracker&lt;>(this);     listeners.add(tracker);     return tracker;   }    void remove(OutputTracker&lt;T> outputTracker) {     listeners.remove(outputTracker);   } }  ---- OutputTracker.java ---- \/\/ Copyright 2022 Titanium I.T. LLC and Ted M. Young. MIT License. package com.jamesshore.output_tracker;  import java.util.ArrayList; import java.util.List;  public class OutputTracker&lt;T> {   private final List&lt;T> output = new ArrayList&lt;>();   private final OutputListener&lt;T> outputListener;    public OutputTracker(OutputListener&lt;T> outputListener) {     this.outputListener = outputListener;   }    void add(T data) {     output.add(data);   }    public List&lt;T> data() {     return List.copyOf(output);   }    public List&lt;T> clear() {     List&lt;T> data = this.data();     output.clear();     return data;   }    public void stop() {     outputListener.remove(this);   } }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"#id8\">\u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses)<\/a>. \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation).<\/p>\n<p><a class=\"anchor\" name=\"id10\" id=\"id10\"><\/a><\/p>\n<h3>\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation)<\/h3>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u043d\u0435 \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u0432\u044b \u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u0435. \u041a\u043e\u0434\u0443, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c, \u043d\u0443\u0436\u0435\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043a\u043e\u0433\u0434\u0430 \u0438\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u044d\u0442\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0441\u043b\u043e\u0436\u043d\u0430 \u0438 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b, \u0438\u043c\u0438\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043e\u0442 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0439\u0442\u0435 \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u0434\u0430 \u0441 \u043a\u043e\u0434\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f. \u041f\u0438\u0448\u0438\u0442\u0435 \u0435\u0433\u043e \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434.<\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <code>MessageServer<\/code> \u0443\u0440\u043e\u0432\u043d\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u0435\u0442\u0435\u0432\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <code>MessageServer<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a \u0432\u0435\u0431-\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>WebSocketServer<\/code>, \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0434\u043b\u044f <a href=\"http:\/\/Socket.IO\">Socket.IO<\/a>. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0430 <code>MessageServer<\/code>, \u0442\u043e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c.<\/p>\n<p>\u0422\u0435\u0441\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432\u0435\u0431-\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 <code>MessageServer<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Output Tracking, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c, \u0447\u0442\u043e \u0431\u044b\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of using Behavior Simulation (JavaScript)  \/\/ Application layer test it(\"broadcasts messages from one client to all others\", async () => {   \/\/ Set up test data   const clientId = \"my_client_id\";   const message = new TestClientMessage(\"my_message\");    \/\/ Set up the infrastructure wrapper and the code under test   const network = WebSocketServer.createNull();   \/\/ Create the infrastructure wrapper   const sentMessages = network.trackMessages();   \/\/ Track messages sent by infrastructure wrapper (Output Tracking)   const server = new MessageServer(network);      \/\/ Instantiate the application code under test   await server.startAsync();                      \/\/ Start listening for messages (Zero-Impact Instantiation)    \/\/ Simulate a client connecting   network.simulateConnection(clientId);    \/\/ Simulate the client sending a message   network.simulateMessage(clientId, message);    \/\/ Check that the message was broadcast (Output Tracking)   assert.deepEqual(sentMessages.data(), [{     type: \"broadcast\",     excludedClient: clientId,     message   }]); });  \/\/ Application layer code class MessageServer {   constructor(webSocketServer) {     this._webSocketServer = webSocketServer;   }    async startAsync() {     \/\/ Code under test     this._webSocketServer.onMessage((clientId, message) => {       this._webSocketServer.broadcastToAllClientsExcept(clientId, message);     });      await this._webSocketServer.startAsync();   }    \/\/... } \/\/ Example of using Behavior Simulation (JavaScript)  \/\/ Application layer test it(\"broadcasts messages from one client to all others\", async () => {   \/\/ Set up test data   const clientId = \"my_client_id\";   const message = new TestClientMessage(\"my_message\");    \/\/ Set up the infrastructure wrapper and the code under test   const network = WebSocketServer.createNull();   \/\/ Create the infrastructure wrapper   const sentMessages = network.trackMessages();   \/\/ Track messages sent by infrastructure wrapper (Output Tracking)   const server = new MessageServer(network);      \/\/ Instantiate the application code under test   await server.startAsync();                      \/\/ Start listening for messages (Zero-Impact Instantiation)    \/\/ Simulate a client connecting   network.simulateConnection(clientId);    \/\/ Simulate the client sending a message   network.simulateMessage(clientId, message);    \/\/ Check that the message was broadcast (Output Tracking)   assert.deepEqual(sentMessages.data(), [{     type: \"broadcast\",     excludedClient: clientId,     message   }]); });  \/\/ Application layer code class MessageServer {   constructor(webSocketServer) {     this._webSocketServer = webSocketServer;   }    async startAsync() {     \/\/ Code under test     this._webSocketServer.onMessage((clientId, message) => {       this._webSocketServer.broadcastToAllClientsExcept(clientId, message);     });      await this._webSocketServer.startAsync();   }    \/\/... }<\/code><\/pre>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434\u0430 \u0432 <code>WebSocketServer<\/code>, \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0435 <a href=\"http:\/\/Socket.IO\">Socket.IO<\/a>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 <a href=\"http:\/\/Socket.IO\">Socket.IO<\/a> \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442 \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u0443\u044f \u043c\u0435\u0442\u043e\u0434\u044b<em> <\/em><code>#handleXxx()<\/code>.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of implementing Behavior Simulation (JavaScript) import { Server } from \"socket.io\";               \/\/ Socket.IO import { EventEmitter } from \"node:events\";       \/\/ Standard Node.js event library import OutputTracker from \"output_tracker\";       \/\/ Output tracking library  const CLIENT_MESSAGE_EVENT = \"client_message\";    \/\/ Event constants const SERVER_MESSAGE_EVENT = \"server_message\";  class WebSocketServer {   static create(port) {     return new WebSocketServer(io, port);   }    static createNull() {     return new WebSocketServer(StubbedServer, 42);   }    constructor(server, port) {     this._server = server;     this._port = port;     this._emitter = new EventEmitter();     this._connectedSockets = {};   }    \/\/ Real Socket.IO event handler   async startAsync() {     this._io.on(\"connection\", (socket) => {       this.#handleConnection(socket);       socket.onAny((event, ...args) => {         const message = this.#deserializeMessage(event, args);         this.#handleMessage(socket.id, message);       });       socket.on(\"disconnect\", () => {         this.#handleDisconnection(socket.id));       });     });   }    \/\/ Behavior Simulation   simulateConnection(clientId) {     this.#handleConnection(new StubbedSocket(clientId));   }    simulateMessage(clientId, message) {     this.#handleMessage(clientId, message);   }    simulateDisconnection(clientId) {     this.#handleDisconnection(clientId);   }    \/\/ Shared by event handler and behavior simulation   #handleConnection(socket) {     this._connectedSockets[socket.id] = socket;   )    #handleMessage(clientId, message) {     this._emitter.emit(CLIENT_MESSAGE_EVENT, { clientId, message });   }    #handleDisconnection(clientId) {     delete this._connectedSockets(clientId);   }    \/\/ Methods called by the code under test   onMessage(fn) {     this._emitter.on(CLIENT_MESSAGE_EVENT, ({ clientId, message }) => {       fn(clientId, message));     });   }    broadcastToAllClientsExcept(clientId, message) {     const socket = this._connectedSockets[clientId];     socket.broadcast.emit(message.name, message.payload);      this._emitter.emit(SERVER_MESSAGE_EVENT, {    \/\/ Output Tracking       type: \"broadcast\",       excludedClient: clientId,       message,     });   });    \/\/ Output Tracking   trackMessages() {     return OutputTracker.create(this._emitter, SERVER_MESSAGE_EVENT);   }    \/\/... }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses). \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0438\u0448\u0443\u0442 \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking).<\/p>\n<p><a class=\"anchor\" name=\"id11\" id=\"id11\"><\/a><\/p>\n<h3>Fake It Once You Make It<\/h3>\n<p>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0438 \u0441\u043b\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c. \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438. \u0425\u043e\u0442\u044f \u043e\u043d\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0434\u043b\u044f \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a, \u043e\u043d\u0438 \u0438\u0437\u043b\u0438\u0448\u043d\u0438 \u0434\u043b\u044f \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u044f\u043c\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043e\u0442 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043a\u0430\u0445 \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u0443\u0439\u0442\u0435 Nullable-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c, \u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0443\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0438 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. \u0412 \u0441\u0432\u043e\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u0432\u043d\u0435\u0434\u0440\u044f\u0439\u0442\u0435 Nulled \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0415\u0441\u043b\u0438 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434\u0435 \u0435\u0441\u0442\u044c \u0444\u0430\u0431\u0440\u0438\u043a\u0430 <code>createNull()<\/code>, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u0435\u0451, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u044f Nulled-\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u0443\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442, \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 <code>LoginClient<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0433\u043e <code>HttpClient<\/code>. <code>LoginClient<\/code> \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u0441 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c Fake It Once You Make It, \u0432\u043d\u0435\u0434\u0440\u044f\u044f \u0432 <code>LoginClient<\/code> Nulled-\u0432\u0435\u0440\u0441\u0438\u044e <code>HttpClient<\/code>.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of a using Fake It Once You Make It in a test (JavaScript)  it(\"performs network request\", async () => {   \/\/ Set up the low-level HTTP client (Configurable Responses)   const httpClient = HttpClient.createNull({     \"\/oauth\/token\": [{                        \/\/ The Auth0 endpoint our code will call.       status: VALID_STATUS,                   \/\/ Status, headers, and body Auth0 could really return.       headers: VALID_HEADERS,       body: VALID_BODY,     }],   });    \/\/ Track requests made with the HTTP client (Output Tracking)   const httpRequests = httpClient.trackRequests();    \/\/ Instantiate the code under test, injecting the Nulled httpClient   const client = new LoginClient(httpClient, \"my_client_id\", \"my_client_secret\", \"my_management_api_token\");    \/\/ Run the code under test   await client.validateLoginAsync(\"my_login_code\", \"my_callback_url\");    \/\/ Assert that the correct HTTP request was made (Output Tracking)   assert.deepEqual(httpRequests.data, [{     host: HOST,     port: PORT,     method: \"post\",     path: \"\/oauth\/token\",     headers: {       authorization: \"Bearer my_management_api_token\",       \"content-type\": \"application\/json; charset=utf-8\",     },     body: JSON.stringify({       client_id: \"my_client_id\",       client_secret: \"my_client_secret\",       code: \"my_login_code\",       redirect_uri: \"my_callback_url\",       grant_type: \"authorization_code\"     }),   }]); });<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434 <code>LoginClient<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439 \u0434\u043b\u044f Auth0, \u0441\u043b\u0443\u0436\u0431\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. <code>LoginClient.createNull()<\/code> \u0438\u043c\u0435\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses), \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0430\u0434\u0440\u0435\u0441\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. \u041e\u043d\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u043f\u0443\u0442\u0451\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f Nulled <code>HttpClient<\/code> \u0438 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 <code>LoginClient<\/code> \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 HTTP-\u043e\u0442\u0432\u0435\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 Auth0 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of a using Fake It Once You Make It to make a class Nullable (JavaScript) class LoginClient {    \/\/ Normal factory   static create(host, clientId, clientSecret, managementApiToken) {     const httpClient = HttpClient.create();     return new LoginClient(httpClient, host, clientId, clientSecret, managementApiToken);   }    \/\/ Null factory with Configurable Responses   static createNull({     \/\/ Configurable Responses for user\u2019s login     email = \"null_login_email\",   \/\/ The email address associated with the login     emailVerified = true,         \/\/ True if the email address has been verified     forbidden = undefined,        \/\/ Set to a string to simulate an Auth0 \"forbidden\" response      \/\/ Other parameters unrelated to Configurable Responses     host = \"null.host\",     clientId = \"null_client_id\",     clientSecret = \"null_client_secret\",     managementApiToken = \"null_management_api_token\",   } = {}) {     \/\/ Convert LoginClient's Configurable Response into the response Auth0 would actually return     const auth0Response = nullValidateLoginResponse({ email, emailVerified, forbidden });      \/\/ Create a Nulled HttpClient that's configured to return the Auth0 response     const httpClient = HttpClient.createNull({       [VALIDATE_LOGIN_ENDPOINT]: auth0Response;     });      \/\/ Instantiate the LoginClient using the Nulled HttpClient     return new LoginClient(httpClient, clientId, clientSecret, managementApiToken);   }    \/\/ Shared initialization   constructor(httpClient, host, clientId, clientSecret, managementApiToken) {     this._httpClient = httpClient;     this._host = host;     this._clientId = clientId;     this._clientSecret = clientSecret;     this._authHeaders = {       authorization: `Bearer ${managementApiToken}`,     };   }    \/\/ Shared production code   async validateLoginAsync(code, callbackUrl) {     const response = await this._httpClient.requestAsync(       host: this._host,       method: \"POST\",       path: VALIDATE_LOGIN_ENDPOINT,       headers: this._authHeaders,       body: {         client_id: this._clientId,         client_secret: this._clientSecret,         code,         redirect_uri: callbackUrl,         grant_type: \"authorization_code\"       },     );      const decodedToken = \/* code to validate and decode response here *\/      return {       email: decodedToken.email,       emailVerified: decodedToken.email_verified     };   }    \/\/ Configurable Responses translation code   \/\/ This function decomposes the responses passed to LoginClient.createNull() down   \/\/ into responses for HttpClient.createNull(). HttpClient.createNull() is configured   \/\/ with a status, optional headers, and a body.   function nullValidateLoginResponse({ email, emailVerified, forbidden }) {     \/\/ If the \"forbidden\" response is set, return a 403 (Forbidden) response.     if (forbidden) return { status: STATUS.FORBIDDEN_403, body: forbidden };      \/\/ Otherwise, create a JSON Web Token, because that's what Auth0 returns     const response = { email, email_verified: emailVerified };     const id_token = jwt.sign(response, \"irrelevant_secret\", { noTimestamp: true });      \/\/ Return the JWT in a 200 (OK) response     return {       status: STATUS.OK_200,       body: JSON.stringify({ id_token }),     };   } }<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0439\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking) \u0438 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation) \u0432 \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435, \u043d\u0435 \u043e\u0431\u0440\u0430\u0449\u0430\u044f \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043d\u0430 \u0442\u043e, \u043c\u043e\u0433\u0443\u0442 \u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 null \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0433\u043b\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 null, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043b\u0438\u0431\u043e <a href=\"#id13\">Descend the Ladder<\/a>, \u043b\u0438\u0431\u043e <a href=\"#id14\">Climb the Ladder<\/a> (\u043e \u043d\u0438\u0445 \u043d\u0438\u0436\u0435).<\/p>\n<p><a class=\"anchor\" name=\"id12\" id=\"id12\"><\/a><\/p>\n<h3>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043b\u0435\u0433\u0430\u0441\u0438-\u043a\u043e\u0434\u0430<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0438 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Nullables, \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u043f\u043e\u043c\u043e\u0433\u0443\u0442 \u0432\u0430\u043c \u0432 \u044d\u0442\u043e\u043c.<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u0442\u044c Nullables \u0441 \u0432\u0430\u0448\u0438\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u0432 \u043e\u0434\u043d\u043e\u0439 \u0438 \u0442\u043e\u0439 \u0436\u0435 \u043a\u043e\u0434\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u0435 \u0438 \u0434\u0430\u0436\u0435 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438 \u0442\u043e\u043c \u0436\u0435 \u0442\u0435\u0441\u0442\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0451 \u0441\u0440\u0430\u0437\u0443. \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u044c\u0442\u0435 \u0441\u0432\u043e\u0438 \u0443\u0441\u0438\u043b\u0438\u044f \u043d\u0430 \u043a\u043e\u0434\u0435, \u0433\u0434\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441 Nullables \u043f\u0440\u0438\u043d\u0435\u0441\u0451\u0442 \u043e\u0449\u0443\u0442\u0438\u043c\u0443\u044e \u043f\u043e\u043b\u044c\u0437\u0443. \u041d\u0435 \u0442\u0440\u0430\u0442\u044c\u0442\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0442\u0430\u043a \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u2014 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043e\u043d \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f.<\/p>\n<ul>\n<li>\n<p>Descend the Ladder (\u00ab\u0421\u043f\u0443\u0441\u043a \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/p>\n<\/li>\n<li>\n<p><a href=\"#id14\">Climb the Ladder (\u00ab\u041f\u043e\u0434\u044a\u0451\u043c \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id15\">\u0417\u0430\u043c\u0435\u043d\u0430 \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id16\">\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 (Throwaway Stub)<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"id13\" id=\"id13\"><\/a><\/p>\n<h3>Descend the Ladder (\u00ab\u0421\u043f\u0443\u0441\u043a \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/h3>\n<p>\u0421\u043b\u043e\u0436\u043d\u044b\u0435 \u043a\u043e\u0434\u043e\u0432\u044b\u0435 \u0431\u0430\u0437\u044b \u0438\u043c\u0435\u044e\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0438 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u0437\u0430 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u2014 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u0440\u043e\u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u041f\u0440\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u0438\u043b\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Nullables \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u043a\u043e\u0434 \u0438 \u0435\u0433\u043e \u043f\u0440\u044f\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043d\u043e \u043d\u0435 \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e. \u0420\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u043d\u0430\u0434 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0434\u0435\u0440\u0435\u0432\u043e\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u2014 \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0438\u043b\u0438 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0435, \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u044c\u0441\u044f \u043a \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0442\u0440\u0435\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439:<\/p>\n<p><strong>A. \u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/strong><\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u043e\u0434 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0442\u0442\u0435\u0440\u043d Nullables. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<p><strong>B. \u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438\u00a0<\/strong><\/p>\n<p>\u0415\u0441\u043b\u0438 \u043a\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0441 \u043f\u0440\u044f\u043c\u044b\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438 \u043e\u0442 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0435\u0433\u043e Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a.<\/p>\n<p><strong>C. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435<\/strong><\/p>\n<p>\u0414\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u0442\u0435 \u043f\u0440\u044f\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u043e\u0434\u0430 Nullable, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435\u0441\u044c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c Fake It Once You Make It. \u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0433\u043b\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>null<\/code>, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043a \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432:<\/p>\n<ul>\n<li>\n<p>\u0412 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0443\u043d\u043a\u0442\u044b. \u0415\u0441\u043b\u0438 \u043d\u0438 \u043e\u0434\u0438\u043d \u0438\u0437 \u043d\u0438\u0445 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c, \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c Nullable, \u0432\u0432\u0435\u0434\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0443\u0436\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f Nullable \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u0443 \u043d\u0435\u0451 \u043d\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f Nullable, \u043d\u043e \u0432\u0441\u0435 \u0435\u0451 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u2014 Nullable, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d Faking It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0435\u0451 Nullable, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0439 \u043a\u043e\u0434 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u0438\u0437\u0432\u043b\u0435\u043a\u0438\u0442\u0435 \u0435\u0433\u043e \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u043d\u043e\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0435\u0451 Nullable, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 Fake It Once You Make It. (\u0415\u0441\u043b\u0438 \u0432 \u043a\u043e\u0434\u0435 \u0435\u0441\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430, \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0435\u0451). \u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043c\u043e\u043a\u0438 \u043d\u0430 Nullables \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u043e \u043c\u0435\u0440\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u0435, \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0435\u0442\u0435, \u0431\u0443\u0434\u0435\u0442 Nullable \u0438 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d. \u0415\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0431\u0443\u0434\u0443\u0442 Nullable, \u043d\u043e \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b. \u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0433\u043e\u0442\u043e\u0432\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u043b\u0438 \u043c\u043e\u0434\u0443\u043b\u044c, \u0441\u043d\u043e\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Descend the Ladder. \u0421\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0432\u044b \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0435 \u0432\u0441\u044e \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443.<\/p>\n<h4>\u041f\u0440\u0438\u043c\u0435\u0440<\/h4>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 <code>Router<\/code> \u2192 <code>LoginController<\/code> \u2192 <code>Auth0Client<\/code> \u2192 <code>HttpClient<\/code>, \u0433\u0434\u0435 <code>HttpClient<\/code> \u2014 \u044d\u0442\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <em>Router<\/em>, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u044f\u043c\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c\u044e <code>Router<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>LoginController<\/code>, \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u043c\u0435\u0448\u0430\u043d\u044b \u043b\u043e\u0433\u0438\u043a\u0430 \u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430. \u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>LoginController<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>Router<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>Router<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u043c\u0435\u043d\u044b \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u043e\u0437\u0436\u0435, \u0435\u0441\u043b\u0438 \u0431\u044b \u0432\u044b \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <code>Auth0Client<\/code>, \u0432\u044b \u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u044f\u043c\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c\u044e <code>Auth0Client<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>HttpClient<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>HttpClient<\/code> Nullable, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>Auth0Client<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>Auth0Client<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u043c\u0435\u043d\u044b \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<\/ol>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <code>LoginController<\/code>, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u044f\u043c\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c\u044e <code>LoginController<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>Auth0Client<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u0440\u0430\u043d\u0435\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d, \u0442\u0430\u043a \u0447\u0442\u043e \u043e\u043d \u0443\u0436\u0435 Nullable.<\/p>\n<\/li>\n<li>\n<p>\u0423 <code>LoginController<\/code> \u0435\u0441\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430, \u043e\u0441\u0442\u0430\u0432\u0448\u0430\u044f\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f <code>Router<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 <code>Auth0Client<\/code> \u0441\u0442\u0430\u043b Nullable, \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u043d\u0430 Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>LoginController<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u043c\u0435\u043d\u044b \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<\/ol>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0433\u043e\u0442\u043e\u0432\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <code>HttpClient<\/code>, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><code>HttpClient<\/code> \u2014 \u044d\u0442\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u0438 \u043e\u043d\u0430 \u0441\u0442\u0430\u043b\u0430 Nullable \u043f\u0440\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0438 <code>Auth0Client<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0451 \u043d\u0443\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 <code>HttpClient<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c, \u043d\u0435 \u043d\u0430\u0440\u0443\u0448\u0430\u044f \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u043e\u0432. \u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430, \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b A-Frame \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b.<\/p>\n<p><a href=\"#id13\">Descend the Ladder (\u00ab\u0421\u043f\u0443\u0441\u043a \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/a> \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u043a\u043e\u0434\u0430 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0434\u0435\u0440\u0435\u0432\u044c\u044f\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0415\u0441\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c\u044b\u0439 \u043a\u043e\u0434 \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"#id14\">Climb the Ladder (\u00ab\u041f\u043e\u0434\u044a\u0451\u043c \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/a>.<\/p>\n<p><a class=\"anchor\" name=\"id14\" id=\"id14\"><\/a><\/p>\n<h3>Climb the Ladder\u00a0<\/h3>\n<p><a href=\"#id13\">Descend the Ladder (\u00ab\u0421\u043f\u0443\u0441\u043a \u043f\u043e \u043b\u0435\u0441\u0442\u043d\u0438\u0446\u0435\u00bb)<\/a> \u2014 \u044d\u0442\u043e \u0442\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439, \u043c\u0435\u0442\u043e\u0434\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u0434\u0430. \u041e\u0434\u043d\u0430\u043a\u043e \u043e\u043d \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a, \u0447\u0442\u043e \u0440\u0430\u0441\u0442\u043e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0434\u0435\u0440\u0435\u0432\u044c\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0442\u0430\u043a\u043e\u0439 \u0442\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u0448\u0435 \u0434\u0435\u0440\u0435\u0432\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0432\u0441\u0451 \u0434\u0435\u0440\u0435\u0432\u043e \u0441\u0440\u0430\u0437\u0443. \u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0434\u0435\u0440\u0435\u0432\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c, \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0417\u0430\u0442\u0435\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0437\u0435\u043b, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u043d\u0438\u0436\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0434\u0435\u0440\u0435\u0432\u0430. (\u041e\u0431\u0445\u043e\u0434 \u0434\u0435\u0440\u0435\u0432\u0430 \u0432 \u0433\u043b\u0443\u0431\u0438\u043d\u0443 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430). \u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0443\u0437\u043b\u0443 \u043e\u0434\u043d\u0443 \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u043f\u0446\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u2014 \u0447\u0438\u0441\u0442\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430, \u0431\u0435\u0437 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043e\u043d \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/798771\/#id31\">Easily-Visible Behavior (\u00ab\u041b\u0435\u0433\u043a\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u043c\u044b\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c\u00bb)<\/a>, \u0430 \u0437\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u043e \u043c\u0435\u0440\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u0443\u0436\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f Nullable, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u044b, \u0437\u0430\u043c\u0435\u043d\u0438\u0432 \u043c\u043e\u043a\u0438 \u043d\u0430 Nullables \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0442\u0435\u0441\u0442\u044b \u043f\u043e \u043c\u0435\u0440\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439<em> \u0438<\/em> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0435\u0433\u043e Nullable, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b <em>\u043d\u0435<\/em> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439, \u043d\u043e <em>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442<\/em> \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0438\u0437\u0432\u043b\u0435\u043a\u0438\u0442\u0435 \u043a\u043e\u0434 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0432 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443. \u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043a \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0435, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u043d\u0438\u0436\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043a \u043e\u0441\u0442\u0430\u0432\u0448\u0435\u043c\u0443\u0441\u044f \u043a\u043e\u0434\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0437\u0435\u043b \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439 \u0438 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434, \u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d Fake It Once You Make It. \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u044b, \u0437\u0430\u043c\u0435\u043d\u0438\u0432 \u043c\u043e\u043a\u0438 \u043d\u0430 Nullables \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0442\u0435\u0441\u0442\u044b \u043f\u043e \u043c\u0435\u0440\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u0435, \u0432\u0441\u0451 \u0434\u0435\u0440\u0435\u0432\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0438 Nullable. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b A-Frame \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 <code>Router<\/code><em> \u2192 <\/em><code>LoginController<\/code><em> \u2192 <\/em><code>Auth0Client<\/code><em> \u2192 <\/em><code>HttpClient<\/code>, \u0433\u0434\u0435 <code>HttpClient<\/code> \u2014 \u044d\u0442\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c <code>Router<\/code>, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><code>HttpClient<\/code> \u2014 \u044d\u0442\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0435\u0433\u043e Nullable, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 <code>HttpClient<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>Auth0Client<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>Auth0Client<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u043c\u0435\u043d\u044b \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>LoginController<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>LoginController<\/code> \u0432 \u0437\u0430\u043c\u0435\u043d\u0443 \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 <code>Router<\/code> Nullable \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u044b <code>Router<\/code> \u0432 \u0437\u0430\u043c\u0435\u043d\u0443 \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables.<\/p>\n<\/li>\n<\/ol>\n<p>Climb the Ladder \u043b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 \u0434\u0435\u0440\u0435\u0432\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435. \u0415\u0441\u043b\u0438 \u0436\u0435 \u043e\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435, \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Descend the Ladder.<\/p>\n<p><a class=\"anchor\" name=\"id15\" id=\"id15\"><\/a><\/p>\n<h3>\u0417\u0430\u043c\u0435\u043d\u0430 \u043c\u043e\u043a\u043e\u0432 \u043d\u0430 Nullables<\/h3>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0447\u0430\u0441\u0442\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u043a\u043e\u0432, \u0448\u043f\u0438\u043e\u043d\u043e\u0432 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u043e\u0432. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u043c\u0435\u0448\u0430\u0442\u044c. \u0418\u0445 \u0431\u044b\u0432\u0430\u0435\u0442 \u0442\u0440\u0443\u0434\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c, \u0438\u043b\u0438 \u043e\u043d\u0438 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0442\u0440\u0443\u0434\u043d\u044f\u0442\u044c \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442 \u043c\u0435\u0448\u0430\u0435\u0442 \u0432\u0430\u043c, \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Nullables. \u0412 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u043e\u0432, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0431\u043b\u043e\u043a\u0438 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b. \u0417\u0430\u0442\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043c\u043e\u043a\u0443, \u0448\u043f\u0438\u043e\u043d\u0443 \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c\u0443 \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u0443 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0435\u0441\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0437\u0430\u043c\u0435\u043d\u044b \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u0430 \u043d\u0430 Nulled \u0432\u0435\u0440\u0441\u0438\u044e \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u0432\u043e\u0439\u043d\u0438\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 Configurable Responses (\u00ab\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b\u00bb).<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u0432\u043e\u0439\u043d\u0438\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043d\u0430 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u043a\u0430\u043a \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u0432\u043e\u0439\u043d\u0438\u043a, \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0435\u0433\u043e \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u0430 Output Tracking (\u00ab\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430\u00bb). \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0439\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u0438 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043b\u044f \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u043e\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 <code>rot13Client<\/code> \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<pre><code class=\"javascript\">\/\/ Example web page controller (JavaScript + Node.js)  import * as homePageView from \"home_page_view\"; import Rot13Client from \"rot13_client\"; import HttpRequest from \"http_request\"; import WwwConfig from \"www_config\";  export default class HomePageController {   constructor(rot13Client) {     this._rot13Client = rot13Client;   }    \/\/ 'request' is an HttpRequest instance   \/\/ 'config' is a WwwConfig instance   async postAsync(request, config) {     \/\/ Parse the 'text' field from the request's JSON body     const body = await request.readBodyAsync();     const formData = new URLSearchParams(body);      const textFields = formData.getAll(\"text\");     const userInput = textFields[0];      \/\/ Call the web service     const output = await this._rot13Client.transformAsync(config.rot13ServiceHost, userInput);      \/\/ Render the page     return homePageView.homePage(output);   } };<\/code><\/pre>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0448\u043f\u0438\u043e\u043d\u043e\u0432 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u043a\u043e\u0434 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441. \u042d\u0442\u043e\u0442 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043b\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of spy-based test (JavaScript + testdouble.js)    it(\"POST asks ROT-13 service to transform text\", async () => {   \/\/ Create spies   const rot13Client = td.instance(Rot13Client);   const request = td.instance(HttpRequest);   const config = td.instance(WwwConfig);    \/\/ Configure spies   config.rot13ServiceHost = \"my.rot13.host\";   \/\/ rot13ServiceHost is a getter, but testdouble.js can\u2019t configure getters\u2019 responses, so we just set the property directly   td.when(request.readBodyAsync()).thenResolve(\"text=hello%20world\");    \/\/ Run the code under test   const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    \/\/ Check that the web service\u2019s wrapper was called correctly   td.verify(rot13Client.transformAsync(\"my.rot13.host\", \"hello world\")); });<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0448\u043f\u0438\u043e\u043d\u0443 \u0437\u0430 \u0440\u0430\u0437. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u0448\u043f\u0438\u043e\u043d\u0430 <code>HttpRequest<\/code> \u043d\u0430 Configurable Response (\u00ab\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b\u00bb).<\/p>\n<pre><code class=\"javascript\">\/\/ Replace HttpRequest spy (JavaScript + testdouble.js)  it(\"POST asks ROT-13 service to transform text\", async () => {   const rot13Client = td.instance(Rot13Client);    \/\/ Replace the HttpRequest spy with a real HttpRequest. (Nullable with Configurable Responses)   const request = td.instance(HttpRequest);   const request = HttpRequest.createNull({ body: \"text=hello%20world\" });    const config = td.instance(WwwConfig);    config.rot13ServiceHost = \"my.rot13.host\";   td.when(request.readBodyAsync()).thenResolve(\"text=hello%20world\");    \/\/ Old configuration no longer needed    const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    td.verify(rot13Client.transformAsync(\"my.rot13.host\", \"hello world\")); });<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Nullables \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c\u0438 \u0434\u0432\u043e\u0439\u043d\u0438\u043a\u0430\u043c\u0438, \u0442\u0435\u0441\u0442\u044b \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043f\u0440\u043e\u0439\u0434\u0443\u0442 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f. \u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u0448\u043f\u0438\u043e\u043d\u0430 <code>WwwConfig<\/code>:<\/p>\n<pre><code class=\"javascript\">\/\/ Replace WwwConfig spy (JavaScript + testdouble.js)  it(\"POST asks ROT-13 service to transform text\", async () => {   const rot13Client = td.instance(Rot13Client);   const request = HttpRequest.createNull({ body: \"text=hello%20world\" });    \/\/ Replace the WwwConfig spy with a real WwwConfig. (Nullable with Configurable Responses)   const config = td.instance(WwwConfig);   const config = WwwConfig.createNull({ rot13ServiceHost: \"my.rot13.host\" });    config.rot13ServiceHost = \"my.rot13.host\";     \/\/ Old configuration no longer needed (and real WwwConfig doesn't allow property to be set)    const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    td.verify(rot13Client.transformAsync(\"my.rot13.host\", \"hello world\")); });<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c. \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u0448\u043f\u0438\u043e\u043d\u0430 <code>Rot13Client<\/code>:<\/p>\n<pre><code class=\"javascript\">\/\/ Replace Rot13Client spy (JavaScript + testdouble.js)  it(\"POST asks ROT-13 service to transform text\", async () => {   \/\/ Replace the Rot13Client spy with a real Rot13Client. (Nullable)   const rot13Client = td.instance(Rot13Client);   const rot13Client = Rot13Client.createNull();    \/\/ Track the requests made by the Rot13Client. (Output Tracking)   const rot13Requests = rot13Client.trackRequests();    const request = HttpRequest.createNull({ body: \"text=hello%20world\" });   const config = WwwConfig.createNull({ rot13ServiceHost: \"my.rot13.host\" });    const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    \/\/ Replace the method call check with a state-based output check. (Output Tracking)   td.verify(rot13Client.transformAsync(\"my.rot13.host\", \"hello world\"));   assert.deepEqual(rot13Requests, [{     host: \"my.rot13.host\",               text: \"hello world\",               });                                });<\/code><\/pre>\n<p>\u0412\u043e\u0442 \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0434\u0432\u0443\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"javascript\">\/\/ Side-by-side comparison of spy-based test and Nullables-based test (JavaScript + testdouble.js)  \/\/ Interaction-based test using spies it(\"POST asks ROT-13 service to transform text\", async () => {   \/\/ Create dependencies   const rot13Client = td.instance(Rot13Client);   const request = td.instance(HttpRequest);   const config = td.instance(WwwConfig);    \/\/ Configure dependencies   config.rot13ServiceHost = \"my.rot13.host\";   td.when(request.readBodyAsync()).thenResolve(\"text=hello%20world\");    \/\/ Run code under test   const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    \/\/ Check that rot13Client was called   td.verify(rot13Client.transformAsync(\"my.rot13.host\", \"hello world\")); });  \/\/ State-based test using Nullables it(\"POST asks ROT-13 service to transform text\", async () => {   \/\/ Create and configure dependencies   const rot13Client = Rot13Client.createNull();   const rot13Requests = rot13Client.trackRequests();   const request = HttpRequest.createNull({ body: \"text=hello%20world\" });   const config = WwwConfig.createNull({ rot13ServiceHost: \"my.rot13.host\" });    \/\/ Run code under test   const controller = new HomePageController(rot13Client);   await controller.postAsync(request, config);    \/\/ Check that rot13Client made the correct request   assert.deepEqual(rot13Requests, [{     host: \"my.rot13.host\",     text: \"hello world\",   }]); });<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c Nullable, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043b\u0438\u0431\u043e Descend the Ladder, \u043b\u0438\u0431\u043e Climb the Ladder.<\/p>\n<h3>\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c Nullable, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0435\u0451 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0442\u043e\u0436\u0435 Nullable. \u0418\u043d\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u0435\u0451 \u0437\u0430 \u043e\u0434\u0438\u043d \u0440\u0430\u0437. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u0412 \u043a\u043e\u0434\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0434\u0435\u043b\u0430\u0435\u0442\u0435 Nullable, \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c Nullable. \u042d\u0442\u043e \u0440\u0430\u0437\u043e\u0440\u0432\u0451\u0442 \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u043f\u0435\u0440\u0435\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0445\u0441\u044f \u043e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u0430\u0441 \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u043c \u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u043c \u0432 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u0431\u0440\u043e\u0441\u044c\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0438\u0445 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Fake It Once You Make It, \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u0442\u0430\u043d\u0435\u0442 Nullable.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Climb the Ladder.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u2014 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u0435\u0433\u043a\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043b\u0435\u0433\u043a\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0448\u0438\u0440\u043e\u043a\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<hr\/>\n<p>\u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0435\u043c \u043d\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 \u0443\u0440\u043e\u043a\u0438 \u043f\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c JavaScript:<\/p>\n<ul>\n<li>\n<p>13 \u043c\u0430\u0440\u0442\u0430: <a href=\"https:\/\/otus.pw\/Xlo1\/\">\u00ab\u041f\u0438\u0448\u0435\u043c UI-\u0442\u0435\u0441\u0442\u044b \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 Page Object\u00bb<\/a><\/p>\n<\/li>\n<li>\n<p>20 \u043c\u0430\u0440\u0442\u0430: <a href=\"https:\/\/otus.pw\/Xlju\/\">\u00ab\u041f\u0438\u0448\u0435\u043c UI-\u0442\u0435\u0441\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e CodeceptJS\u00bb<\/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\/799939\/\"> https:\/\/habr.com\/ru\/articles\/799939\/<\/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<p>\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 <a href=\"https:\/\/habr.com\/ru\/companies\/otus\/articles\/798771\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.<\/a><\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/h2>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u043c\u0438\u0440\u043e\u043c. \u0425\u043e\u0442\u044f \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043d\u0430 \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439. \u0412\u0441\u0451 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u043a\u043e\u0434\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u0435\u043d \u0438 \u0441 \u0442\u0440\u0443\u0434\u043e\u043c \u043f\u043e\u0434\u0434\u0430\u0451\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0438\u0437-\u0437\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u0420\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 (Infrastructure Wrappers)<\/p>\n<\/li>\n<li>\n<p><a href=\"#id2\">\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b (Narrow Integration Tests)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id3\">\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb (Paranoic Telemetry)<\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"id1\" id=\"id1\"><\/a><\/p>\n<h3>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438<\/h3>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c, \u0441\u043b\u043e\u0436\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0447\u0430\u0441\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u0418\u0437\u043e\u043b\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043e. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u2014 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u2014 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043a\u043b\u0430\u0441\u0441-\u043e\u0431\u0451\u0440\u0442\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u044d\u0442\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0447\u0451\u0442\u043a\u043e\u0435 \u0438 \u0447\u0438\u0441\u0442\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0435\u0441\u043f\u043e\u0440\u044f\u0434\u043e\u0447\u043d\u043e\u0433\u043e \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u043c\u0438\u0440\u0430 \u0432 \u0442\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/p>\n<p>\u0418\u0437\u0431\u0435\u0433\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0412 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u043e\u0431\u0449\u0438\u0445 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>LoginClient<\/code> \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 <code>HttpClient<\/code>. \u0412 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043b\u0430\u0441\u0441 <code>DataStore<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <code>RelationalDb<\/code> \u0438 \u043a\u043b\u0430\u0441\u0441\u0430 <code>NoSqlDb<\/code>. \u0417\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043e\u0434\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0446\u0435\u043f\u043e\u0447\u0435\u043a \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 \u00ab\u043f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb. \u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0438\u0445 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"#id4\">\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432 Nullability.<\/a><\/p>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u00ab<a href=\"https:\/\/martinfowler.com\/articles\/gateway-pattern.html\">\u0448\u043b\u044e\u0437\u0430\u043c\u0438<\/a>\u00bb \u0438\u043b\u0438 \u00ab\u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430\u043c\u0438\u00bb, \u0445\u043e\u0442\u044f \u044d\u0442\u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u044b \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0434\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a.<\/p>\n<p><a class=\"anchor\" name=\"id2\" id=\"id2\"><\/a><\/p>\n<h3>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b<\/h3>\n<p>\u0412 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0441\u0447\u0451\u0442\u0435, \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0441\u0435\u0442\u044c\u044e, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0438\u043b\u0438 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442 \u043a\u0430\u043a\u0443\u044e-\u043b\u0438\u0431\u043e \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0432\u044f\u0437\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c. \u0417\u0434\u0435\u0441\u044c \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e-\u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c\u0443. \u0414\u043b\u044f \u043a\u043e\u0434\u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0447\u0438\u0442\u0430\u0439\u0442\u0435 \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043e\u0431\u0440\u0430\u0449\u0430\u0439\u0442\u0435\u0441\u044c \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0442\u0443 \u0436\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u0447\u0442\u043e \u0438 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u0441\u0440\u0435\u0434\u0430. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u043e\u043b\u043a\u043d\u0451\u0442\u0441\u044f \u0441 \u0442\u043e\u043d\u043a\u0438\u043c\u0438 \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0439\u0442\u0435 \u0443\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445, \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435. \u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0430 \u0438\u0445 \u0437\u0430\u043f\u0443\u0441\u043a \u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u0443\u0434\u0443\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0438\u043b\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c \u0441\u0431\u043e\u0440\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0441\u0431\u043e\u044f\u043c\u0438 \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043b\u044e\u0434\u044c\u043c\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043e\u0431\u0449\u0443\u044e \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u0434\u043b\u044f \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438. \u0417\u0430\u0442\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0412\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u043d\u0435 \u043d\u0443\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435<a href=\"https:\/\/www.jamesshore.com\/v2\/projects\/nullables\/testing-without-mocks#fake-it\"> <\/a>\u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0443\u0442\u044c \u043a \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 <a href=\"#id11\">Fake It Once You Make It<\/a>, \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u0432 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0439 \u043e\u0431\u0451\u0440\u0442\u043a\u0435.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 <code>LoginClient<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u0433\u043e <code>HttpClient<\/code>. <code>LoginClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 Fake It Once You Make It, \u0430 <code>HttpClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0437\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of narrow integration tests for HttpClient (JavaScript + Node.js) import * as http from \"node:http\"; import HttpClient from \".\/http_client\";  const HOST = \"localhost\"; const PORT = 5001;  \/\/ Tests describe(\"HTTP Client\", () => {   let server;    before(async () => {     server = new TestServer();     await server.startAsync();   });    after(async () => {     await server.stopAsync();   });    beforeEach(function() {     server.reset();   });    it(\"performs request\", async () => {     await requestAsync({       host: HOST,       port: PORT,       method: \"POST\",       path: \"\/my\/path\",       headers: { myRequestHeader: \"myRequestValue\" },       body: \"my request body\"     });      assert.deepEqual(server.lastRequest, {       method: \"POST\",       path: \"\/my\/path\",       headers: { myrequestheader: \"myRequestValue\" },       body: \"my request body\"     });   });    it(\"returns response\", async () => {     server.setResponse({       status: 999,       headers: { myResponseHeader: \"myResponseValue\" },       body: \"my response\",     });      const response = await requestAsync();     assert.deepEqual(response, {       status: 999,       headers: { myresponseheader: \"myResponseValue\" },       body: \"my response\",     });   });    async function requestAsync(options = {     host: HOST,     port: PORT,     method: \"GET\",     path: \"\/irrelevant\/path\",   }) {     const client = HttpClient.create();     return client.requestAsync(options);   }  });  \/\/ Localhost HTTP server class TestServer {   constructor() {     this.reset();   }    reset() {     this._lastRequest = null;     this._nextResponse = {       status: 500,       headers: {},       body: \"response not specified\",     };   }    startAsync() {     return new Promise((resolve, reject) => {       this._server = http.createServer();       this._server.once(\"listening\", resolve);       this._server.once(\"error\", reject);       this._server.on(\"request\", this.#handleRequest.bind(this));       this._server.listen(PORT);     });   }    stopAsync() {     return new Promise((resolve, reject) => {       this._server.once(\"close\", resolve);       this._server.close();     });   }    setResponse(response) {     this._nextResponse = response;   }    get lastRequest() {     return this._lastRequest;   }    \/\/ In JavaScript, methods that start with \"#\" are private.   #handleRequest(request, response) {     let body = \"\";     request.on(\"data\", (chunk) => {       body += chunk;     });     request.on(\"end\", () => {       this.#storeRequest(request, body);       this.#sendResponse(response);     });   }    #storeRequest(request, body) {     const headers = { ...request.headers };     delete headers.connection;     delete headers[\"content-length\"];     delete headers.host;      this._lastRequest = {       method: request.method,       path: request.url,       headers,       body,     };   }    #sendResponse(response) {     response.statusCode = this._nextResponse.status;     Object.entries(this._nextResponse.headers).forEach(([key, value]) => {       response.setHeader(key, value);     });      response.end(this._nextResponse.body);   } } <\/code><\/pre>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u00ab\u043f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u043e\u0439 \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u0438\u00bb \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043a\u043e\u0434 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435.<\/p>\n<p><a class=\"anchor\" name=\"id3\" id=\"id3\"><\/a><\/p>\n<h3>\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb (Paranoic Telemetry)<\/h3>\n<p>\u0412\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u043d\u044b. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0432 \u0447\u0451\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c, \u2014 \u044d\u0442\u043e \u0438\u0445 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u043a\u0430\u0437. \u0424\u0430\u0439\u043b\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0442\u0435\u0440\u044f\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438. \u0421\u043b\u0443\u0436\u0431\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u043a\u043e\u0434\u044b \u043e\u0448\u0438\u0431\u043e\u043a, \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u0432\u043e\u0438 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443:<\/em><\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0439\u0442\u0435, \u0447\u0442\u043e \u043e\u043d\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u044f\u0442 \u0432\u0430\u0441 \u0434\u043e\u0441\u0442\u0430\u0442\u044c, \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0439 \u043a\u043e\u0434 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0414\u043e\u043f\u0443\u0441\u043a\u0430\u0439\u0442\u0435, \u0447\u0442\u043e \u0440\u0430\u043d\u043e \u0438\u043b\u0438 \u043f\u043e\u0437\u0434\u043d\u043e \u0432\u0441\u0451 \u0441\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0441\u0431\u043e\u044f \u043b\u0438\u0431\u043e \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435, \u043b\u0438\u0431\u043e \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043b\u043e\u0433\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0434\u0430 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0448\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>\u0412\u0441\u0435 \u044d\u0442\u0438 \u0441\u043b\u0443\u0447\u0430\u0438 \u043e\u0442\u043a\u0430\u0437\u0430 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0437\u0430\u0442\u0440\u0430\u0442 \u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0438 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u0435. \u041f\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0430 \u043d\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b.<\/p>\n<p>\u00ab\u041f\u0430\u0440\u0430\u043d\u043e\u0438\u0434\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044f\u00bb \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0430 <a href=\"https:\/\/martinfowler.com\/bliki\/ContractTest.html\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/a>. \u042d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u0438\u0445 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a (\u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0432\u044b), \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0443\u043b\u043e\u0432\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0433\u043e\u043d\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p><a class=\"anchor\" name=\"id4\" id=\"id4\"><\/a><\/p>\n<h2>\u041f\u0430\u0442\u0442\u0435\u0440\u043d\u044b Nullability<\/h2>\n<p>\u041e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434. \u042d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u043b\u044f \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a, \u043d\u043e \u0435\u0441\u043b\u0438 \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u2014 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u2014 \u0438\u043c\u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u0440\u0443\u0434\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c. \u00ab\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u00bb \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u043e\u0431\u0449\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>Nullables<\/p>\n<\/li>\n<li>\n<p><a href=\"#id6\">\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 (Embedded Stub)<\/a> <\/p>\n<\/li>\n<li>\n<p><a href=\"#id7\">\u0422\u043e\u043d\u043a\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430 (Thin Wrapper)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id8\">\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b (Configurable Responses)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id9\">\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 (Output Tracking)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id10\">\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (Behavior Simulation)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#id11\">\u201cFake It Once You Make It\u201d <\/a><\/p>\n<\/li>\n<\/ul>\n<p><a class=\"anchor\" name=\"id5\" id=\"id5\"><\/a><\/p>\n<h3>Nullables<\/h3>\n<p>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b \u0438 \u0441\u043b\u043e\u0436\u043d\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435. \u0425\u043e\u0442\u044f \u043e\u043d\u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a, \u043e\u043d\u0438 \u0438\u0437\u043b\u0438\u0448\u043d\u0438 \u0434\u043b\u044f \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u0438\u0445 \u043e\u0431\u0451\u0440\u0442\u043e\u043a. <em>\u041f\u043e\u044d\u0442\u043e\u043c\u0443<\/em>:<\/p>\n<p>\u041f\u043b\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u043e\u0434, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0438\u043c\u0435\u043b \u0444\u0430\u0431\u0440\u0438\u0447\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>createNull()<\/code>. \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 &#171;Nulled&#187;, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432\u0441\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u043e \u0432\u0435\u0434\u0451\u0442 \u0441\u0435\u0431\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u0432\u043e \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044f\u0445 <em>(2)<\/em>. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043e\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0430\u043d\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.<\/p>\n<p><em>(2). Nullables \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0438 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c Null Object, \u043d\u043e \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u0438 \u0441\u0442\u0430\u043b\u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<\/em><\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u044b\u0437\u043e\u0432 <code>LoginClient.createNull().getUserInfo(...)<\/code><em> <\/em>\u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0435\u0437 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>Nullables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d \u043a\u043e\u0434 \u0438 \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u0425\u043e\u0442\u044f Nulled \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445, \u043e\u043d\u0438 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u00ab\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u00bb \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Nullables \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u043f\u0446\u0438\u044e \u00ab\u0444\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u043e\u043d\u0430\u00bb \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<pre><code class=\"javascript\">\/\/ Example of using Nullables to implement \"dry run\" option (JavaScript + Node.js) async initializeGitWriter(config) {   if (config.dryRun) {     return GitWriter.createNull();   }   else {     return GitWriter.create();   } }<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Nullables <\/p>\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-370674","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/370674","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=370674"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/370674\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=370674"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=370674"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=370674"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}