{"id":60,"date":"2026-03-26T22:37:00","date_gmt":"2026-03-26T14:37:00","guid":{"rendered":"https:\/\/www.deartester.com\/?p=60"},"modified":"2026-06-15T11:39:39","modified_gmt":"2026-06-15T03:39:39","slug":"%e4%bb%8e%e9%9b%b6%e5%bc%80%e5%a7%8b%e6%8e%a5%e5%8f%a3%e8%87%aa%e5%8a%a8%e5%8c%96%e6%b5%8b%e8%af%95%e6%a1%86%e6%9e%b6%e6%90%ad%e5%bb%ba","status":"publish","type":"post","link":"https:\/\/www.deartester.com\/index.php\/2026\/03\/26\/%e4%bb%8e%e9%9b%b6%e5%bc%80%e5%a7%8b%e6%8e%a5%e5%8f%a3%e8%87%aa%e5%8a%a8%e5%8c%96%e6%b5%8b%e8%af%95%e6%a1%86%e6%9e%b6%e6%90%ad%e5%bb%ba\/","title":{"rendered":"\u4ece\u96f6\u5f00\u59cb\u63a5\u53e3\u81ea\u52a8\u5316\u6d4b\u8bd5\u6846\u67b6\u642d\u5efa"},"content":{"rendered":"<div class=\"wp-block-post-date\"><time datetime=\"2026-03-26T22:37:00+08:00\">2026\u5e743\u670826\u65e5<\/time><\/div>\n\n<div class=\"wp-block-post-author-name\">\u6d4b\u8bd5\u738b\u540c\u5b66<\/div>\n\n\n<h2 class=\"wp-block-heading\">\u5199\u5728\u524d\u9762<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u505a\u4e86\u4e24\u4e09\u5e74\u529f\u80fd\u6d4b\u8bd5\u7684\u540c\u5b66\uff0c\u5927\u6982\u7387\u4f1a\u9047\u5230\u8fd9\u6837\u7684\u573a\u666f\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6bcf\u6b21\u53d1\u7248\uff0c\u56de\u5f52\u7528\u4f8b\u4e24\u767e\u591a\u6761\uff0c\u624b\u5de5\u70b9\u70b9\u70b9\u8dd1\u5230\u6000\u7591\u4eba\u751f<\/li>\n\n\n\n<li>\u6539\u4e86\u4e00\u4e2a\u5b57\u6bb5\uff0c\u4e0d\u786e\u5b9a\u5f71\u54cd\u8303\u56f4\uff0c\u53ea\u80fd\u628a\u76f8\u5173\u63a5\u53e3\u5168\u8dd1\u4e00\u904d<\/li>\n\n\n\n<li>\u4e0a\u7ebf\u540e\u53d1\u73b0\u63a5\u53e3\u6302\u4e86\uff0c\u800c\u4f60\u7684\u6d4b\u8bd5\u7528\u4f8b\u91cc\u660e\u660e\u8986\u76d6\u4e86\u2014\u2014\u4f46\u4f60\u4e0d\u786e\u5b9a\u662f\u4e0d\u662f\u6700\u65b0\u7248\u672c\u7684\u6570\u636e\u8dd1\u7684<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u4e9b\u95ee\u9898\uff0c\u63a5\u53e3\u81ea\u52a8\u5316\u90fd\u80fd\u89e3\u51b3\u3002\u4e0d\u662f\u90a3\u79cd&#8221;\u5b66\u4e86\u9762\u8bd5\u80fd\u5439\u4e00\u5439&#8221;\u7684\u89e3\u51b3\uff0c\u800c\u662f\u5b9e\u6253\u5b9e\u5730<strong>\u6bcf\u5929\u5e2e\u4f60\u7701\u4e24\u4e09\u4e2a\u5c0f\u65f6<\/strong>\u7684\u90a3\u79cd\u89e3\u51b3\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4f46\u5f88\u591a\u540c\u5b66\u60f3\u5b66\uff0c\u6253\u5f00\u7f51\u4e0a\u4e00\u641c\uff0c\u5404\u79cd\u6846\u67b6\u4e94\u82b1\u516b\u95e8\uff0c\u770b\u5b8c\u8fd8\u662f\u4e0d\u77e5\u9053\u600e\u4e48\u5f00\u59cb\u3002\u4eca\u5929\u8fd9\u7bc7\u6587\u7ae0\uff0c\u5c31\u5e26\u4f60<strong>\u4ece\u96f6\u5f00\u59cb\u3001\u4e00\u6b65\u6b65\u642d\u4e00\u5957\u5b8c\u6574\u7684\u63a5\u53e3\u81ea\u52a8\u5316\u6846\u67b6<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e0d\u7528\u82b1\u54e8\u7684\u4e1c\u897f\uff0c\u4f7f\u7528 Python + Pytest + Requests\uff0c\u5b9e\u7528\u3001\u597d\u61c2\u3001\u80fd\u843d\u5730<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e00\u3001\u6846\u67b6\u6574\u4f53\u8bbe\u8ba1\u601d\u8def<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5728\u52a8\u624b\u5199\u4ee3\u7801\u4e4b\u524d\uff0c\u5148\u60f3\u6e05\u695a\u4e00\u4ef6\u4e8b\uff1a<strong>\u6846\u67b6\u8981\u5206\u5c42<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ec0\u4e48\u662f\u5206\u5c42\uff1f\u5c31\u662f\u8ba9\u4e0d\u540c\u7684\u4e8b\u60c5\u5404\u53f8\u5176\u804c\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">\u5c42\u7ea7<\/th><th class=\"has-text-align-left\" data-align=\"left\">\u804c\u8d23<\/th><th class=\"has-text-align-left\" data-align=\"left\">\u4e3e\u4e2a\u4f8b\u5b50<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>\u7528\u4f8b\u5c42<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">\u53ea\u7ba1\u5199\u6d4b\u8bd5\u573a\u666f<\/td><td class=\"has-text-align-left\" data-align=\"left\">\u767b\u5f55\u63a5\u53e3\u6b63\u5e38\u767b\u5f55\u3001\u5bc6\u7801\u9519\u8bef\u767b\u5f55<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>\u4e1a\u52a1\u5c42<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">\u5c01\u88c5\u63a5\u53e3\u8c03\u7528\u903b\u8f91<\/td><td class=\"has-text-align-left\" data-align=\"left\">login(username, password)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>\u8bf7\u6c42\u5c42<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">\u5c01\u88c5 HTTP \u8bf7\u6c42<\/td><td class=\"has-text-align-left\" data-align=\"left\">\u53d1 GET\/POST \u8bf7\u6c42\u3001\u7edf\u4e00\u5904\u7406\u54cd\u5e94<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>\u6570\u636e\u5c42<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">\u7ba1\u7406\u6d4b\u8bd5\u6570\u636e\u548c\u914d\u7f6e<\/td><td class=\"has-text-align-left\" data-align=\"left\">URL\u3001\u73af\u5883\u53d8\u91cf\u3001\u7528\u4f8b\u6570\u636e<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e3a\u4ec0\u4e48\u8981\u5206\u8fd9\u4e48\u7ec6\uff1f<strong>\u6539\u4e00\u4e2a\u5730\u65b9\u4e0d\u5f71\u54cd\u5176\u4ed6\u5730\u65b9<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bd4\u5982\u63a5\u53e3\u5730\u5740\u53d8\u4e86\uff0c\u53ea\u6539\u914d\u7f6e\u6587\u4ef6\uff1b\u6bd4\u5982\u8bf7\u6c42\u8981\u52a0\u7edf\u4e00\u7684 header\uff0c\u53ea\u6539\u8bf7\u6c42\u5c01\u88c5\uff1b\u6bd4\u5982\u8981\u52a0\u65b0\u7528\u4f8b\uff0c\u53ea\u52a0\u7528\u4f8b\u6587\u4ef6\u3002\u5206\u5c42\u4e4b\u540e\uff0c\u7ef4\u62a4\u6210\u672c\u4f1a\u5927\u5e45\u964d\u4f4e\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e00\u53e5\u8bdd\u603b\u7ed3\uff1a<strong>\u7528\u4f8b\u53ea\u5173\u5fc3&#8221;\u6d4b\u4ec0\u4e48&#8221;\uff0c\u4e0d\u5173\u5fc3&#8221;\u600e\u4e48\u53d1\u8bf7\u6c42&#8221;\u3002<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e8c\u3001\u6280\u672f\u9009\u578b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u6280\u672f\u9009\u578b\u8fd9\u4ef6\u4e8b\uff0c\u7ea0\u7ed3\u7684\u4eba\u7279\u522b\u591a\u3002\u5176\u5b9e\u5bf9\u4e8e\u5927\u591a\u6570\u56e2\u961f\u6765\u8bf4\uff0c\u7b54\u6848\u5f88\u7b80\u5355\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Python<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6d4b\u8bd5\u9886\u57df\u7b2c\u4e00\u8bed\u8a00\u3002\u751f\u6001\u597d\u3001\u5b66\u4e60\u6210\u672c\u4f4e\u3001\u4e0a\u624b\u5feb\u3002\u5c31\u7b97\u4f60\u4e4b\u524d\u53ea\u4f1a\u4e00\u70b9 Java \u6216\u5b8c\u5168\u96f6\u57fa\u7840\uff0cPython \u4e5f\u6bd4\u5176\u4ed6\u8bed\u8a00\u66f4\u5bb9\u6613\u4e0a\u624b\u505a\u81ea\u52a8\u5316\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pytest<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Python \u6d4b\u8bd5\u6846\u67b6\u7684\u5929\u82b1\u677f\u3002\u6bd4\u8d77 unittest\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5199\u6cd5\u66f4\u7b80\u6d01\uff08\u4e0d\u7528\u5199 class\uff09<\/li>\n\n\n\n<li>\u53c2\u6570\u5316\u4e00\u884c\u641e\u5b9a\uff08<code>@pytest.mark.parametrize<\/code>\uff09<\/li>\n\n\n\n<li>\u63d2\u4ef6\u751f\u6001\u5f3a\u5927\uff08Allure \u62a5\u544a\u3001\u5e76\u53d1\u6267\u884c\u3001\u91cd\u8bd5\u673a\u5236\u5168\u6709\uff09<\/li>\n\n\n\n<li>\u652f\u6301 fixture\uff0c\u505a\u6570\u636e\u51c6\u5907\u548c\u6e05\u7406\u975e\u5e38\u65b9\u4fbf<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Requests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u53d1 HTTP \u8bf7\u6c42\u7684\u5e93\uff0c\u6ca1\u6709\u4e4b\u4e00\u3002API \u8bbe\u8ba1\u4f18\u96c5\uff0c\u51e0\u884c\u4ee3\u7801\u5c31\u80fd\u641e\u5b9a\u4e00\u6b21\u8bf7\u6c42\u3002\u4e0d\u7528\u7ea0\u7ed3\u7528\u4ec0\u4e48 HTTP \u5e93\uff0cRequests \u5c31\u662f\u6807\u51c6\u7b54\u6848\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">YAML \/ Excel<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6d4b\u8bd5\u6570\u636e\u7ba1\u7406\u3002YAML \u9002\u5408\u914d\u7f6e\u548c\u73af\u5883\u4fe1\u606f\uff0c\u7ed3\u6784\u6e05\u6670\uff1bExcel \u9002\u5408\u7ba1\u7406\u5927\u91cf\u6d4b\u8bd5\u7528\u4f8b\u6570\u636e\uff0c\u56e2\u961f\u4e2d\u975e\u6280\u672f\u4eba\u5458\u4e5f\u80fd\u7ef4\u62a4\u3002<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>\u4e3a\u4ec0\u4e48\u4e0d\u9009 Postman\/Newman\uff1f<\/strong>&nbsp;Postman \u9002\u5408\u5feb\u901f\u8c03\u8bd5\u548c\u5c0f\u89c4\u6a21\u63a5\u53e3\u6d4b\u8bd5\u3002\u4f46\u7528\u4f8b\u591a\u4e86\u4e4b\u540e\uff0c\u7ef4\u62a4\u56f0\u96be\u3001\u96be\u4ee5\u505a\u6570\u636e\u9a71\u52a8\u3001\u4e0d\u65b9\u4fbf CI\/CD \u96c6\u6210\u3002\u4ee3\u7801\u6846\u67b6\u7684\u7075\u6d3b\u6027\u548c\u53ef\u6269\u5c55\u6027\u662f Postman \u6bd4\u4e0d\u4e86\u7684\u3002<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e09\u3001\u6846\u67b6\u76ee\u5f55\u7ed3\u6784\u8be6\u89e3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5148\u770b\u6574\u4f53\u7ed3\u6784\uff0c\u518d\u9010\u4e2a\u89e3\u91ca\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>api_autotest\/<br>\u251c\u2500\u2500 config\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # \u914d\u7f6e\u6587\u4ef6<br>\u2502 &nbsp; \u251c\u2500\u2500 config.yaml &nbsp; &nbsp; &nbsp; &nbsp; # \u73af\u5883\u914d\u7f6e\uff08URL\u3001\u6570\u636e\u5e93\u7b49\uff09<br>\u2502 &nbsp; \u2514\u2500\u2500 __init__.py<br>\u251c\u2500\u2500 data\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # \u6d4b\u8bd5\u6570\u636e<br>\u2502 &nbsp; \u251c\u2500\u2500 test_login.yaml &nbsp; &nbsp; # \u767b\u5f55\u63a5\u53e3\u6d4b\u8bd5\u6570\u636e<br>\u2502 &nbsp; \u2514\u2500\u2500 __init__.py<br>\u251c\u2500\u2500 common\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # \u516c\u5171\u6a21\u5757<br>\u2502 &nbsp; \u251c\u2500\u2500 base_api.py &nbsp; &nbsp; &nbsp; &nbsp; # \u8bf7\u6c42\u57fa\u7840\u5c01\u88c5<br>\u2502 &nbsp; \u251c\u2500\u2500 assertions.py &nbsp; &nbsp; &nbsp; # \u65ad\u8a00\u5c01\u88c5<br>\u2502 &nbsp; \u251c\u2500\u2500 logger.py &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # \u65e5\u5fd7\u5de5\u5177<br>\u2502 &nbsp; \u251c\u2500\u2500 db_handler.py &nbsp; &nbsp; &nbsp; # \u6570\u636e\u5e93\u64cd\u4f5c\uff08\u540e\u7eed\u6269\u5c55\uff09<br>\u2502 &nbsp; \u2514\u2500\u2500 __init__.py<br>\u251c\u2500\u2500 testcases\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# \u6d4b\u8bd5\u7528\u4f8b<br>\u2502 &nbsp; \u251c\u2500\u2500 test_login.py &nbsp; &nbsp; &nbsp; # \u767b\u5f55\u63a5\u53e3\u6d4b\u8bd5<br>\u2502 &nbsp; \u2514\u2500\u2500 __init__.py<br>\u251c\u2500\u2500 reports\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# \u6d4b\u8bd5\u62a5\u544a\uff08\u81ea\u52a8\u751f\u6210\uff09<br>\u251c\u2500\u2500 logs\/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # \u65e5\u5fd7\u6587\u4ef6\uff08\u81ea\u52a8\u751f\u6210\uff09<br>\u251c\u2500\u2500 conftest.py &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Pytest \u5168\u5c40 fixture<br>\u251c\u2500\u2500 pytest.ini &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# Pytest \u914d\u7f6e\u6587\u4ef6<br>\u2514\u2500\u2500 requirements.txt &nbsp; &nbsp; &nbsp; &nbsp;# \u4f9d\u8d56\u6e05\u5355<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5404\u76ee\u5f55\u4f5c\u7528\u8bf4\u660e\uff1a<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>config\/<\/strong>\uff1a\u653e\u73af\u5883\u914d\u7f6e\uff0c\u6bd4\u5982\u4e0d\u540c\u73af\u5883\u7684\u63a5\u53e3\u5730\u5740\u3001\u8d26\u53f7\u5bc6\u7801\u7b49\u3002\u5207\u6362\u73af\u5883\u53ea\u9700\u8981\u6539\u4e00\u4e2a\u6587\u4ef6\u3002<\/li>\n\n\n\n<li><strong>data\/<\/strong>\uff1a\u6d4b\u8bd5\u7528\u4f8b\u7684\u8f93\u5165\u6570\u636e\u548c\u9884\u671f\u7ed3\u679c\u3002\u6570\u636e\u4e0e\u7528\u4f8b\u5206\u79bb\uff0c\u6539\u6570\u636e\u4e0d\u7528\u6539\u4ee3\u7801\u3002<\/li>\n\n\n\n<li><strong>common\/<\/strong>\uff1a\u6240\u6709\u516c\u5171\u80fd\u529b\u7684\u5c01\u88c5\u3002\u8bf7\u6c42\u600e\u4e48\u53d1\u3001\u65ad\u8a00\u600e\u4e48\u505a\u3001\u65e5\u5fd7\u600e\u4e48\u8bb0\uff0c\u7edf\u4e00\u7ba1\u7406\u3002<\/li>\n\n\n\n<li><strong>testcases\/<\/strong>\uff1a\u5177\u4f53\u7684\u6d4b\u8bd5\u7528\u4f8b\u3002\u6bcf\u4e2a\u63a5\u53e3\u4e00\u4e2a\u6587\u4ef6\uff08\u6216\u6309\u6a21\u5757\u5206\u6587\u4ef6\uff09\uff0c\u53ea\u5199\u4e1a\u52a1\u903b\u8f91\u3002<\/li>\n\n\n\n<li><strong>reports\/<\/strong>\uff1aAllure \u751f\u6210\u7684\u62a5\u544a\u5b58\u653e\u4f4d\u7f6e\u3002<\/li>\n\n\n\n<li><strong>logs\/<\/strong>\uff1a\u8fd0\u884c\u65e5\u5fd7\u5b58\u653e\u4f4d\u7f6e\u3002<\/li>\n\n\n\n<li><strong>conftest.py<\/strong>\uff1aPytest \u7684\u5168\u5c40\u914d\u7f6e\uff0c\u653e fixture\u3001\u94a9\u5b50\u51fd\u6570\u7b49\u3002<\/li>\n\n\n\n<li><strong>pytest.ini<\/strong>\uff1aPytest \u7684\u8fd0\u884c\u914d\u7f6e\uff0c\u6307\u5b9a\u53c2\u6570\u548c\u89c4\u5219\u3002<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u56db\u3001\u6838\u5fc3\u4ee3\u7801\u5b9e\u73b0<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e0b\u9762\u8fdb\u5165\u6b63\u9898\uff0c\u628a\u6838\u5fc3\u6a21\u5757\u4e00\u4e2a\u4e2a\u5199\u51fa\u6765\u3002\u6240\u6709\u4ee3\u7801\u90fd\u662f<strong>\u5b8c\u6574\u53ef\u8fd0\u884c<\/strong>\u7684\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 \u914d\u7f6e\u6587\u4ef6<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5148\u5efa\u4e00\u4e2a YAML \u914d\u7f6e\u6587\u4ef6\uff0c\u7edf\u4e00\u7ba1\u7406\u73af\u5883\u4fe1\u606f\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># config\/config.yaml<br>env:test<br><br>test:<br>base_url:\"https:\/\/reqres.in\/api\"<br>timeout:10<br><br>uat:<br>base_url:\"https:\/\/uat.example.com\/api\"<br>timeout:10<br><br>production:<br>base_url:\"https:\/\/api.example.com\/api\"<br>timeout:30<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u7136\u540e\u7528\u4e00\u4e2a\u5de5\u5177\u51fd\u6570\u6765\u8bfb\u53d6\u914d\u7f6e\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/config_reader.py<br>import&nbsp;yaml<br>import&nbsp;os<br><br>classConfigReader:<br>def__init__(self):<br>&nbsp; &nbsp; &nbsp; &nbsp; config_path = os.path.join(os.path.dirname(__file__),&nbsp;'..',&nbsp;'config',&nbsp;'config.yaml')<br>withopen(config_path,&nbsp;'r', encoding='utf-8')&nbsp;as&nbsp;f:<br>self.config = yaml.safe_load(f)<br><br>defget(self, key, default=None):<br>\"\"\"\u83b7\u53d6\u6307\u5b9a\u914d\u7f6e\u9879\"\"\"<br>returnself.config.get(key, default)<br><br>defget_env_config(self):<br>\"\"\"\u83b7\u53d6\u5f53\u524d\u73af\u5883\u7684\u914d\u7f6e\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; env =&nbsp;self.config.get('env',&nbsp;'test')<br>returnself.config.get(env, {})<br><br># \u5168\u5c40\u5b9e\u4f8b\uff0c\u76f4\u63a5\u5bfc\u5165\u4f7f\u7528<br>conf = ConfigReader()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 \u8bf7\u6c42\u5c01\u88c5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u662f\u6846\u67b6\u7684\u6838\u5fc3\u3002\u5bf9 Requests \u505a\u4e8c\u6b21\u5c01\u88c5\uff0c\u7edf\u4e00\u5904\u7406\u8bf7\u6c42\u5934\u3001\u8d85\u65f6\u3001\u65e5\u5fd7\u548c\u5f02\u5e38\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/base_api.py<br>import&nbsp;requests<br>import&nbsp;logging<br>from&nbsp;common.config_reader&nbsp;import&nbsp;conf<br><br>logger = logging.getLogger(__name__)<br><br>classBaseAPI:<br>def__init__(self):<br>self.base_url = conf.get_env_config().get('base_url',&nbsp;'')<br>self.timeout = conf.get_env_config().get('timeout',&nbsp;10)<br>self.session = requests.Session()<br># \u9ed8\u8ba4\u8bf7\u6c42\u5934\uff0c\u6240\u6709\u8bf7\u6c42\u90fd\u4f1a\u5e26\u4e0a<br>self.session.headers.update({<br>'Content-Type':&nbsp;'application\/json',<br>&nbsp; &nbsp; &nbsp; &nbsp; })<br><br>defsend_request(self, method, path, **kwargs):<br>\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; \u7edf\u4e00\u8bf7\u6c42\u5165\u53e3<br>&nbsp; &nbsp; &nbsp; &nbsp; :param method: GET\/POST\/PUT\/DELETE<br>&nbsp; &nbsp; &nbsp; &nbsp; :param path: \u63a5\u53e3\u8def\u5f84\uff0c\u5982 \/users<br>&nbsp; &nbsp; &nbsp; &nbsp; :param kwargs: requests \u652f\u6301\u7684\u6240\u6709\u53c2\u6570\uff08params, json, data, headers\u7b49\uff09<br>&nbsp; &nbsp; &nbsp; &nbsp; \"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; url =&nbsp;self.base_url + path<br><br># \u8bbe\u7f6e\u9ed8\u8ba4\u8d85\u65f6\uff0c\u5141\u8bb8\u5355\u4e2a\u7528\u4f8b\u8986\u76d6<br>&nbsp; &nbsp; &nbsp; &nbsp; kwargs.setdefault('timeout',&nbsp;self.timeout)<br><br>&nbsp; &nbsp; &nbsp; &nbsp; logger.info(f\"\u8bf7\u6c42 &gt;&gt;&gt;&nbsp;{method}{url}\")<br>if&nbsp;kwargs.get('json'):<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.info(f\"\u8bf7\u6c42\u4f53 &gt;&gt;&gt;&nbsp;{kwargs&#91;'json']}\")<br><br>try:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response =&nbsp;self.session.request(method, url, **kwargs)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.info(f\"\u54cd\u5e94 &lt;&lt;&lt; \u72b6\u6001\u7801:&nbsp;{response.status_code}\")<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.info(f\"\u54cd\u5e94\u4f53 &lt;&lt;&lt;&nbsp;{response.text&#91;:500]}\")<br>return&nbsp;response<br>except&nbsp;requests.exceptions.Timeout:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.error(f\"\u8bf7\u6c42\u8d85\u65f6:&nbsp;{method}{url}\")<br>raise<br>except&nbsp;requests.exceptions.ConnectionError:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logger.error(f\"\u8fde\u63a5\u5931\u8d25:&nbsp;{method}{url}\")<br>raise<br><br>defget(self, path, **kwargs):<br>returnself.send_request('GET', path, **kwargs)<br><br>defpost(self, path, **kwargs):<br>returnself.send_request('POST', path, **kwargs)<br><br>defput(self, path, **kwargs):<br>returnself.send_request('PUT', path, **kwargs)<br><br>defdelete(self, path, **kwargs):<br>returnself.send_request('DELETE', path, **kwargs)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.3 \u6d4b\u8bd5\u6570\u636e\u7ba1\u7406<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7528 YAML \u6587\u4ef6\u7ba1\u7406\u6d4b\u8bd5\u6570\u636e\uff0c\u5b9e\u73b0<strong>\u6570\u636e\u4e0e\u4ee3\u7801\u5206\u79bb<\/strong>\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># data\/test_login.yaml<br>login_success:<br>username:\"eve.holt@reqres.in\"<br>password:\"cityslicka\"<br>expected_code:200<br>expected_key:\"token\"<br><br>login_fail_wrong_password:<br>username:\"eve.holt@reqres.in\"<br>password:\"wrong_password\"<br>expected_code:400<br>expected_message:\"Invalid credentials\"<br><br>login_fail_missing_password:<br>username:\"eve.holt@reqres.in\"<br>password:\"\"<br>expected_code:400<br>expected_message:\"Missing password\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u8bfb\u53d6\u6d4b\u8bd5\u6570\u636e\u7684\u5de5\u5177\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/data_loader.py<br>import&nbsp;yaml<br>import&nbsp;os<br><br>defload_yaml_data(filename):<br>\"\"\"\u52a0\u8f7d YAML \u6d4b\u8bd5\u6570\u636e\u6587\u4ef6\"\"\"<br>&nbsp; &nbsp; file_path = os.path.join(os.path.dirname(__file__),&nbsp;'..',&nbsp;'data', filename)<br>withopen(file_path,&nbsp;'r', encoding='utf-8')&nbsp;as&nbsp;f:<br>return&nbsp;yaml.safe_load(f)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.4 \u65ad\u8a00\u5c01\u88c5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u628a\u5e38\u7528\u7684\u65ad\u8a00\u5c01\u88c5\u8d77\u6765\uff0c\u8ba9\u7528\u4f8b\u91cc\u7684\u65ad\u8a00\u4ee3\u7801\u66f4\u7b80\u6d01\uff0c\u800c\u4e14\u62a5\u9519\u4fe1\u606f\u66f4\u53cb\u597d\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/assertions.py<br>import&nbsp;logging<br><br>logger = logging.getLogger(__name__)<br><br>defassert_status_code(response, expected_code):<br>\"\"\"\u65ad\u8a00\u54cd\u5e94\u72b6\u6001\u7801\"\"\"<br>&nbsp; &nbsp; actual_code = response.status_code<br>assert&nbsp;actual_code == expected_code, \\<br>f\"\u72b6\u6001\u7801\u4e0d\u5339\u914d: \u671f\u671b&nbsp;{expected_code}, \u5b9e\u9645&nbsp;{actual_code}\"<br><br>defassert_response_contains(response, key):<br>\"\"\"\u65ad\u8a00\u54cd\u5e94\u4f53\u5305\u542b\u6307\u5b9a\u5b57\u6bb5\"\"\"<br>&nbsp; &nbsp; data = response.json()<br>assert&nbsp;key&nbsp;in&nbsp;data, \\<br>f\"\u54cd\u5e94\u4f53\u4e2d\u7f3a\u5c11\u5b57\u6bb5 '{key}'\uff0c\u5b9e\u9645\u54cd\u5e94:&nbsp;{data}\"<br><br>defassert_equal(actual, expected, msg=\"\"):<br>\"\"\"\u65ad\u8a00\u76f8\u7b49\"\"\"<br>assert&nbsp;actual == expected, \\<br>f\"{msg}&nbsp;\u671f\u671b:&nbsp;{expected}, \u5b9e\u9645:&nbsp;{actual}\"if&nbsp;msg&nbsp;else&nbsp;\\<br>f\"\u503c\u4e0d\u5339\u914d: \u671f\u671b&nbsp;{expected}, \u5b9e\u9645&nbsp;{actual}\"<br><br>defassert_response_time(response, max_seconds):<br>\"\"\"\u65ad\u8a00\u54cd\u5e94\u65f6\u95f4\"\"\"<br>&nbsp; &nbsp; elapsed = response.elapsed.total_seconds()<br>assert&nbsp;elapsed &lt;= max_seconds, \\<br>f\"\u54cd\u5e94\u8d85\u65f6:&nbsp;{elapsed}s &gt;&nbsp;{max_seconds}s\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.5 \u6d4b\u8bd5\u7528\u4f8b\u7f16\u5199<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u524d\u9762\u6240\u6709\u5c01\u88c5\u90fd\u662f\u4e3a\u4e86\u8fd9\u4e00\u6b65\u2014\u2014<strong>\u7528\u4f8b\u5199\u5f97\u5e72\u51c0\u3001\u6e05\u723d<\/strong>\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># testcases\/test_login.py<br>import&nbsp;pytest<br>from&nbsp;common.base_api&nbsp;import&nbsp;BaseAPI<br>from&nbsp;common.assertions&nbsp;import&nbsp;assert_status_code, assert_response_contains, assert_equal<br>from&nbsp;common.data_loader&nbsp;import&nbsp;load_yaml_data<br>from&nbsp;common.logger&nbsp;import&nbsp;setup_logger<br><br># \u52a0\u8f7d\u6d4b\u8bd5\u6570\u636e<br>test_data = load_yaml_data('test_login.yaml')<br>setup_logger()<br><br>classTestLogin:<br>\"\"\"\u767b\u5f55\u63a5\u53e3\u6d4b\u8bd5\"\"\"<br><br>defsetup_class(self):<br>self.api = BaseAPI()<br><br>deftest_login_success(self):<br>\"\"\"\u6b63\u5e38\u767b\u5f55 - \u9a8c\u8bc1\u8fd4\u56de token\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; data = test_data&#91;'login_success']<br><br>&nbsp; &nbsp; &nbsp; &nbsp; response =&nbsp;self.api.post('\/login', json={<br>'email': data&#91;'username'],<br>'password': data&#91;'password']<br>&nbsp; &nbsp; &nbsp; &nbsp; })<br><br>&nbsp; &nbsp; &nbsp; &nbsp; assert_status_code(response, data&#91;'expected_code'])<br>&nbsp; &nbsp; &nbsp; &nbsp; assert_response_contains(response, data&#91;'expected_key'])<br>print(f\"\\n \u767b\u5f55\u6210\u529f\uff0c\u83b7\u53d6\u5230 token:&nbsp;{response.json()&#91;'token']}\")<br><br>deftest_login_wrong_password(self):<br>\"\"\"\u5bc6\u7801\u9519\u8bef\u767b\u5f55\u5931\u8d25\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; data = test_data&#91;'login_fail_wrong_password']<br><br>&nbsp; &nbsp; &nbsp; &nbsp; response =&nbsp;self.api.post('\/login', json={<br>'email': data&#91;'username'],<br>'password': data&#91;'password']<br>&nbsp; &nbsp; &nbsp; &nbsp; })<br><br>&nbsp; &nbsp; &nbsp; &nbsp; assert_status_code(response, data&#91;'expected_code'])<br>&nbsp; &nbsp; &nbsp; &nbsp; assert_equal(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.json().get('error'),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data&#91;'expected_message'],<br>\"\u9519\u8bef\u4fe1\u606f\u4e0d\u5339\u914d\"<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br><br>deftest_login_missing_password(self):<br>\"\"\"\u7f3a\u5c11\u5bc6\u7801\u53c2\u6570\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; data = test_data&#91;'login_fail_missing_password']<br><br>&nbsp; &nbsp; &nbsp; &nbsp; response =&nbsp;self.api.post('\/login', json={<br>'email': data&#91;'username'],<br>'password': data&#91;'password']<br>&nbsp; &nbsp; &nbsp; &nbsp; })<br><br>&nbsp; &nbsp; &nbsp; &nbsp; assert_status_code(response, data&#91;'expected_code'])<br>&nbsp; &nbsp; &nbsp; &nbsp; assert_equal(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.json().get('error'),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data&#91;'expected_message'],<br>\"\u9519\u8bef\u4fe1\u606f\u4e0d\u5339\u914d\"<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br><br>deftest_login_response_time(self):<br>\"\"\"\u54cd\u5e94\u65f6\u95f4\u4e0d\u5e94\u8d85\u8fc7 2 \u79d2\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; data = test_data&#91;'login_success']<br><br>&nbsp; &nbsp; &nbsp; &nbsp; response =&nbsp;self.api.post('\/login', json={<br>'email': data&#91;'username'],<br>'password': data&#91;'password']<br>&nbsp; &nbsp; &nbsp; &nbsp; })<br><br>&nbsp; &nbsp; &nbsp; &nbsp; assert_response_time(response,&nbsp;2)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.6 \u65e5\u5fd7\u914d\u7f6e<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6ca1\u6709\u65e5\u5fd7\u7684\u6846\u67b6\u7b49\u4e8e\u6ca1\u6709\u76d1\u63a7\u7684\u773c\u775b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/logger.py<br>import&nbsp;logging<br>import&nbsp;os<br><br>defsetup_logger():<br>\"\"\"\u914d\u7f6e\u5168\u5c40\u65e5\u5fd7\"\"\"<br>&nbsp; &nbsp; log_dir = os.path.join(os.path.dirname(__file__),&nbsp;'..',&nbsp;'logs')<br>&nbsp; &nbsp; os.makedirs(log_dir, exist_ok=True)<br><br>&nbsp; &nbsp; formatter = logging.Formatter(<br>'%(asctime)s &#91;%(levelname)s] %(name)s - %(message)s',<br>&nbsp; &nbsp; &nbsp; &nbsp; datefmt='%Y-%m-%d %H:%M:%S'<br>&nbsp; &nbsp; )<br><br># \u6587\u4ef6\u65e5\u5fd7<br>&nbsp; &nbsp; file_handler = logging.FileHandler(<br>&nbsp; &nbsp; &nbsp; &nbsp; os.path.join(log_dir,&nbsp;'test_run.log'), encoding='utf-8'<br>&nbsp; &nbsp; )<br>&nbsp; &nbsp; file_handler.setLevel(logging.DEBUG)<br>&nbsp; &nbsp; file_handler.setFormatter(formatter)<br><br># \u63a7\u5236\u53f0\u65e5\u5fd7<br>&nbsp; &nbsp; console_handler = logging.StreamHandler()<br>&nbsp; &nbsp; console_handler.setLevel(logging.INFO)<br>&nbsp; &nbsp; console_handler.setFormatter(formatter)<br><br># \u6839\u65e5\u5fd7\u5668\u914d\u7f6e<br>&nbsp; &nbsp; root_logger = logging.getLogger()<br>&nbsp; &nbsp; root_logger.setLevel(logging.DEBUG)<br>&nbsp; &nbsp; root_logger.addHandler(file_handler)<br>&nbsp; &nbsp; root_logger.addHandler(console_handler)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.7 Pytest \u914d\u7f6e<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># pytest.ini<br>&#91;pytest]<br>testpaths&nbsp;= testcases<br>python_files&nbsp;= test_*.py<br>python_classes&nbsp;= Test*<br>python_functions&nbsp;= test_*<br>addopts&nbsp;= -v -s --tb=short<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e94\u3001\u8fd0\u884c\u548c\u62a5\u544a\u751f\u6210<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">5.1 \u5b89\u88c5\u4f9d\u8d56<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># requirements.txt<br>pytest==8.1.1<br>requests==2.31.0<br>PyYAML==6.0.1<br>allure-pytest==2.13.5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u6267\u884c\u5b89\u88c5\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install -r requirements.txt<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5.2 \u8fd0\u884c\u6d4b\u8bd5<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># \u8fd0\u884c\u6240\u6709\u7528\u4f8b<br>pytest<br><br># \u8fd0\u884c\u6307\u5b9a\u6587\u4ef6<br>pytest testcases\/test_login.py<br><br># \u8fd0\u884c\u6307\u5b9a\u7528\u4f8b<br>pytest testcases\/test_login.py::TestLogin::test_login_success<br><br># \u5931\u8d25\u65f6\u81ea\u52a8\u91cd\u8bd5\uff08\u9700\u5b89\u88c5 pytest-rerunfailures\uff09<br>pytest --reruns 2<br><br># \u5e76\u53d1\u6267\u884c\uff08\u9700\u5b89\u88c5 pytest-xdist\uff09<br>pytest -n auto<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5.3 \u751f\u6210 Allure \u62a5\u544a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Allure \u662f\u76ee\u524d\u6700\u597d\u770b\u7684\u6d4b\u8bd5\u62a5\u544a\u6846\u67b6\uff0c\u6ca1\u6709\u4e4b\u4e00\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u751f\u6210 Allure \u6570\u636e\uff08\u5728 reports\/allure-raw \u76ee\u5f55\uff09<br>pytest --alluredir=reports\/allure-raw<br><br># \u542f\u52a8\u62a5\u544a\u670d\u52a1\uff08\u6d4f\u89c8\u5668\u81ea\u52a8\u6253\u5f00\uff09<br>allure serve reports\/allure-raw<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Allure \u62a5\u544a\u652f\u6301\u7684\u7279\u6027\u5305\u62ec\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u7528\u4f8b\u901a\u8fc7\u7387\u7edf\u8ba1<\/li>\n\n\n\n<li>\u5386\u53f2\u8d8b\u52bf\u56fe<\/li>\n\n\n\n<li>\u7528\u4f8b\u6309\u529f\u80fd\u6a21\u5757\u5206\u7ec4<\/li>\n\n\n\n<li>\u9644\u4ef6\uff08\u8bf7\u6c42\u65e5\u5fd7\u3001\u54cd\u5e94\u622a\u56fe\uff09<\/li>\n\n\n\n<li>\u5931\u8d25\u7528\u4f8b\u81ea\u52a8\u5f52\u7c7b<\/li>\n\n\n\n<li>\u6807\u7b7e\u7ba1\u7406\uff08\u5192\u70df\u6d4b\u8bd5\u3001\u56de\u5f52\u6d4b\u8bd5\u7b49\uff09<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u5982\u679c\u60f3\u5728 CI\/CD \u4e2d\u96c6\u6210\uff0c\u53ea\u9700\u5728 Jenkins \u6216 GitLab CI \u4e2d\u52a0\u4e0a\u76f8\u540c\u7684\u547d\u4ee4\u5373\u53ef\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u516d\u3001\u6846\u67b6\u6269\u5c55\u65b9\u5411<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u57fa\u7840\u6846\u67b6\u642d\u597d\u4e4b\u540e\uff0c\u6839\u636e\u5b9e\u9645\u9700\u6c42\u9010\u6b65\u6269\u5c55\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6570\u636e\u5e93\u64cd\u4f5c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6d4b\u8bd5\u524d\u540e\u9700\u8981\u51c6\u5907\u548c\u6e05\u7406\u6570\u636e\u65f6\uff0c\u5c01\u88c5\u6570\u636e\u5e93\u64cd\u4f5c\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># common\/db_handler.py<br>import&nbsp;pymysql<br><br>classDBHandler:<br>def__init__(self, host, port, user, password, database):<br>self.conn = pymysql.connect(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; host=host, port=port, user=user,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; password=password, database=database,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br><br>defexecute(self, sql, params=None):<br>withself.conn.cursor()&nbsp;as&nbsp;cursor:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor.execute(sql, params)<br>self.conn.commit()<br>return&nbsp;cursor.fetchall()<br><br>defclose(self):<br>self.conn.close()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u90ae\u4ef6\u901a\u77e5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6d4b\u8bd5\u8dd1\u5b8c\u81ea\u52a8\u53d1\u90ae\u4ef6\u901a\u77e5\u56e2\u961f\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6570\u636e\u9a71\u52a8<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7528&nbsp;<code>@pytest.mark.parametrize<\/code>&nbsp;\u6216 Excel \u5b9e\u73b0\u6570\u636e\u9a71\u52a8\u6d4b\u8bd5\uff0c\u4e00\u5957\u4ee3\u7801\u8dd1\u591a\u7ec4\u6570\u636e\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u5173\u8054\u5904\u7406<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ece\u54cd\u5e94\u4e2d\u63d0\u53d6 token\u3001session \u7b49\u52a8\u6001\u6570\u636e\uff0c\u4f20\u9012\u7ed9\u540e\u7eed\u8bf7\u6c42\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e03\u3001\u5b66\u4e60\u8def\u5f84\u5efa\u8bae<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u6846\u67b6\u642d\u5b8c\u4e0d\u662f\u7ec8\u70b9\uff0c\u800c\u662f\u8d77\u70b9\u3002\u5efa\u8bae\u7684\u5b66\u4e60\u8def\u5f84\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7b2c\u4e00\u6b65\uff1a\u5148\u8dd1\u901a<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u628a\u8fd9\u7bc7\u6587\u7ae0\u7684\u4ee3\u7801\u5168\u90e8\u6572\u4e00\u904d\uff0c\u8dd1\u901a\u7b2c\u4e00\u4e2a\u7528\u4f8b\u3002\u4e0d\u8981\u590d\u5236\u7c98\u8d34\uff0c\u81ea\u5df1\u6572\u4e00\u904d\u624d\u80fd\u7406\u89e3\u6bcf\u4e2a\u6587\u4ef6\u7684\u4f5c\u7528\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8dd1\u901a\u7684\u6807\u51c6\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u80fd\u6210\u529f\u53d1\u9001\u4e00\u4e2a HTTP \u8bf7\u6c42<\/li>\n\n\n\n<li>\u80fd\u770b\u5230\u65e5\u5fd7\u8f93\u51fa<\/li>\n\n\n\n<li>\u80fd\u770b\u5230\u6d4b\u8bd5\u62a5\u544a<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7b2c\u4e8c\u6b65\uff1a\u518d\u52a0\u63a5\u53e3<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u628a\u4f60\u5de5\u4f5c\u4e2d\u6700\u5e38\u89c1\u7684 3-5 \u4e2a\u63a5\u53e3\u52a0\u8fdb\u53bb\u3002\u6bd4\u5982\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u767b\u5f55\u63a5\u53e3<\/li>\n\n\n\n<li>\u67e5\u8be2\u63a5\u53e3<\/li>\n\n\n\n<li>\u65b0\u589e\u63a5\u53e3<\/li>\n\n\n\n<li>\u4fee\u6539\u63a5\u53e3<\/li>\n\n\n\n<li>\u5220\u9664\u63a5\u53e3<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bcf\u4e2a\u63a5\u53e3\u90fd\u6309\u7167 test_login.py \u7684\u6a21\u5f0f\u6765\u5199\uff0c\u6162\u6162\u5c31\u719f\u6089\u4e86\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7b2c\u4e09\u6b65\uff1a\u518d\u5b66\u53c2\u6570\u5316<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e00\u4e2a\u63a5\u53e3\u8981\u6d4b\u591a\u79cd\u573a\u666f\uff0c\u6bd4\u5982\u767b\u5f55\u63a5\u53e3\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6b63\u5e38\u767b\u5f55<\/li>\n\n\n\n<li>\u5bc6\u7801\u9519\u8bef<\/li>\n\n\n\n<li>\u8d26\u53f7\u4e0d\u5b58\u5728<\/li>\n\n\n\n<li>\u5bc6\u7801\u4e3a\u7a7a<\/li>\n\n\n\n<li>\u8d26\u53f7\u4e3a\u7a7a<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u7528&nbsp;<code>@pytest.mark.parametrize<\/code>&nbsp;\u6216\u8005 YAML \u6570\u636e\u6587\u4ef6\uff0c\u4e00\u5957\u4ee3\u7801\u8dd1\u591a\u7ec4\u6570\u636e\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7b2c\u56db\u6b65\uff1a\u518d\u5b66\u5173\u8054<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5f88\u591a\u63a5\u53e3\u4e4b\u95f4\u6709\u4f9d\u8d56\u5173\u7cfb\u3002\u6bd4\u5982\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u767b\u5f55\u540e\u62ff\u5230 token<\/li>\n\n\n\n<li>\u4e0b\u4e00\u4e2a\u63a5\u53e3\u8981\u7528\u8fd9\u4e2a token<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b66\u4f1a\u7528 fixture \u6216\u8005\u53d8\u91cf\u6765\u4f20\u9012\u52a8\u6001\u6570\u636e\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7b2c\u4e94\u6b65\uff1a\u518d\u5b66 CI\/CD<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u628a\u6846\u67b6\u96c6\u6210\u5230 Jenkins \u6216 GitLab CI\uff0c\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4ee3\u7801\u63d0\u4ea4\u540e\u81ea\u52a8\u8fd0\u884c\u6d4b\u8bd5<\/li>\n\n\n\n<li>\u6d4b\u8bd5\u5931\u8d25\u81ea\u52a8\u53d1\u90ae\u4ef6\u901a\u77e5<\/li>\n\n\n\n<li>\u6d4b\u8bd5\u62a5\u544a\u81ea\u52a8\u751f\u6210\u548c\u5f52\u6863<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u516b\u3001\u5e38\u89c1\u95ee\u9898<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q1\uff1a\u4e3a\u4ec0\u4e48\u8981\u5206\u5c42\uff1f\u76f4\u63a5\u5199\u7528\u4f8b\u4e0d\u884c\u5417\uff1f<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u53ef\u4ee5\uff0c\u4f46\u7ef4\u62a4\u6210\u672c\u4f1a\u5f88\u9ad8\u3002\u63a5\u53e3\u5730\u5740\u53d8\u4e86\uff0c\u4f60\u8981\u6539\u6240\u6709\u7528\u4f8b\uff1b\u8bf7\u6c42\u8981\u52a0\u7edf\u4e00 header\uff0c\u4f60\u8981\u6539\u6240\u6709\u7528\u4f8b\u3002\u5206\u5c42\u4e4b\u540e\uff0c\u6539\u4e00\u5904\u5c31\u884c\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q2\uff1a\u4e3a\u4ec0\u4e48\u7528 Pytest \u4e0d\u7528 unittest\uff1f<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pytest \u66f4\u7b80\u6d01\u3001\u529f\u80fd\u66f4\u5f3a\u5927\u3001\u63d2\u4ef6\u751f\u6001\u66f4\u4e30\u5bcc\u3002unittest \u662f Python \u81ea\u5e26\u7684\uff0c\u4f46\u5199\u6cd5\u7e41\u7410\uff0c\u4e0d\u652f\u6301\u5f88\u591a\u73b0\u4ee3\u6d4b\u8bd5\u6846\u67b6\u7684\u7279\u6027\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q3\uff1a\u4e3a\u4ec0\u4e48\u7528 YAML \u4e0d\u7528 Excel\uff1f<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">YAML \u7ed3\u6784\u6e05\u6670\uff0c\u652f\u6301\u5d4c\u5957\uff0c\u9002\u5408\u914d\u7f6e\u548c\u73af\u5883\u4fe1\u606f\u3002Excel \u9002\u5408\u7ba1\u7406\u5927\u91cf\u6d4b\u8bd5\u7528\u4f8b\u6570\u636e\uff0c\u4f46\u9700\u8981\u989d\u5916\u7684\u5e93\u6765\u8bfb\u53d6\u3002\u4e24\u4e2a\u53ef\u4ee5\u7ed3\u5408\u4f7f\u7528\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q4\uff1a\u8fd9\u4e2a\u6846\u67b6\u80fd\u7528\u4e8e\u5b9e\u9645\u9879\u76ee\u5417\uff1f<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u80fd\u3002\u8fd9\u4e2a\u6846\u67b6\u5df2\u7ecf\u5305\u542b\u4e86\u5b9e\u9645\u9879\u76ee\u6700\u6838\u5fc3\u7684\u90e8\u5206\uff1a\u8bf7\u6c42\u5c01\u88c5\u3001\u6570\u636e\u7ba1\u7406\u3001\u65ad\u8a00\u5c01\u88c5\u3001\u65e5\u5fd7\u3001\u62a5\u544a\u3002\u6269\u5c55\u65b9\u5411\u4e5f\u7ed9\u4e86\uff1a\u6570\u636e\u5e93\u64cd\u4f5c\u3001\u90ae\u4ef6\u901a\u77e5\u3001\u6570\u636e\u9a71\u52a8\u3001\u5173\u8054\u5904\u7406\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q5\uff1a\u5b66\u5b8c\u8fd9\u4e2a\u8981\u591a\u4e45\uff1f<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6709 Python \u57fa\u7840\uff1a1-2 \u5468\u80fd\u8dd1\u901a\u5e76\u52a0\u4e0a\u81ea\u5df1\u7684\u63a5\u53e3<\/li>\n\n\n\n<li>\u6ca1\u6709 Python \u57fa\u7840\uff1a\u5148\u82b1 1-2 \u5468\u5b66 Python \u57fa\u7840\uff0c\u518d\u82b1 1-2 \u5468\u5b66\u6846\u67b6<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u4e5d\u3001\u603b\u7ed3<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u7bc7\u6587\u7ae0\uff0c\u6211\u4eec\u4ece\u96f6\u5f00\u59cb\u642d\u4e86\u4e00\u5957\u5b8c\u6574\u7684\u63a5\u53e3\u81ea\u52a8\u5316\u6d4b\u8bd5\u6846\u67b6\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5206\u5c42\u8bbe\u8ba1\u601d\u8def<\/li>\n\n\n\n<li>\u6280\u672f\u9009\u578b\uff08Python + Pytest + Requests + YAML\uff09<\/li>\n\n\n\n<li>\u76ee\u5f55\u7ed3\u6784\u8be6\u89e3<\/li>\n\n\n\n<li>\u6838\u5fc3\u4ee3\u7801\u5b9e\u73b0\uff08\u914d\u7f6e\u3001\u8bf7\u6c42\u3001\u6570\u636e\u3001\u65ad\u8a00\u3001\u7528\u4f8b\u3001\u65e5\u5fd7\uff09<\/li>\n\n\n\n<li>\u8fd0\u884c\u548c\u62a5\u544a\u751f\u6210<\/li>\n\n\n\n<li>\u6269\u5c55\u65b9\u5411<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u5957\u6846\u67b6\u4e0d\u662f\u82b1\u67b6\u5b50\uff0c\u800c\u662f\u771f\u6b63\u80fd\u7528\u4e8e\u5b9e\u9645\u9879\u76ee\u7684\u3002\u4f60\u53ef\u4ee5\u76f4\u63a5\u62ff\u53bb\u7528\uff0c\u7136\u540e\u6839\u636e\u81ea\u5df1\u7684\u9700\u6c42\u9010\u6b65\u6269\u5c55\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b66\u4e60\u81ea\u52a8\u5316\u6d4b\u8bd5\uff0c\u6700\u96be\u7684\u4e0d\u662f\u6280\u672f\uff0c\u800c\u662f<strong>\u5f00\u59cb<\/strong>\u3002\u5f88\u591a\u4eba\u770b\u4e86\u65e0\u6570\u6559\u7a0b\uff0c\u5374\u4ece\u6765\u6ca1\u6709\u52a8\u624b\u5199\u8fc7\u4e00\u884c\u4ee3\u7801\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5e0c\u671b\u8fd9\u7bc7\u6587\u7ae0\u80fd\u5e2e\u4f60\u8fc8\u51fa\u7b2c\u4e00\u6b65<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5199\u5728\u524d\u9762 \u505a\u4e86\u4e24\u4e09\u5e74\u529f\u80fd\u6d4b\u8bd5\u7684\u540c\u5b66\uff0c\u5927\u6982\u7387\u4f1a\u9047\u5230\u8fd9\u6837\u7684\u573a\u666f\uff1a \u8fd9\u4e9b\u95ee\u9898\uff0c\u63a5\u53e3\u81ea\u52a8\u5316\u90fd\u80fd\u89e3\u51b3\u3002\u4e0d\u662f\u90a3\u79cd&#038;#822 &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.deartester.com\/index.php\/2026\/03\/26\/%e4%bb%8e%e9%9b%b6%e5%bc%80%e5%a7%8b%e6%8e%a5%e5%8f%a3%e8%87%aa%e5%8a%a8%e5%8c%96%e6%b5%8b%e8%af%95%e6%a1%86%e6%9e%b6%e6%90%ad%e5%bb%ba\/\" class=\"more-link\">Read more<span class=\"screen-reader-text\"> &#8220;\u4ece\u96f6\u5f00\u59cb\u63a5\u53e3\u81ea\u52a8\u5316\u6d4b\u8bd5\u6846\u67b6\u642d\u5efa&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-60","post","type-post","status-publish","format-standard","hentry","category-auto-tools"],"_links":{"self":[{"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/posts\/60","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/comments?post=60"}],"version-history":[{"count":1,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/posts\/60\/revisions"}],"predecessor-version":[{"id":61,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/posts\/60\/revisions\/61"}],"wp:attachment":[{"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/media?parent=60"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/categories?post=60"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deartester.com\/index.php\/wp-json\/wp\/v2\/tags?post=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}