{"id":1691,"date":"2015-03-14T11:40:27","date_gmt":"2015-03-14T03:40:27","guid":{"rendered":"http:\/\/blog.turn.tw\/?p=1691"},"modified":"2015-03-14T11:40:27","modified_gmt":"2015-03-14T03:40:27","slug":"%e4%bd%a0%e6%9b%bfcontrollers%e5%af%ab%e6%b8%ac%e8%a9%a6%e5%97%8e%ef%bc%9f","status":"publish","type":"post","link":"https:\/\/blog.turn.tw\/?p=1691","title":{"rendered":"\u4f60\u66ffcontrollers\u5beb\u6e2c\u8a66\u55ce\uff1f"},"content":{"rendered":"<p>\u524d\u9663\u5b50\u770b\u5230<a href=\"http:\/\/culttt.com\/2013\/07\/15\/how-to-structure-testable-controllers-in-laravel-4\/\">\u66ffcontrollers\u5beb\u6e2c\u8a66<\/a>\u7684\u6587\u7ae0\u3002\u6478\u4e86\u4e00\u9663\u5b50\u9084\u662f\u6c92\u638c\u63e1\u3002<\/p>\n<p>\u8dd1\u53bbReddit<a href=\"http:\/\/www.reddit.com\/r\/PHP\/comments\/2vjj11\/do_you_write_tests_for_controllers\/\">\u767c\u554f<\/a>\uff0c\u5f97\u5230\u597d\u591a\u56de\u61c9\u3002<\/p>\n<p>\u56e0\u70ba\u7372\u76ca\u826f\u591a\uff0c\u6240\u4ee5\u5728\u6b64\u7ffb\u8b6f\u8a0e\u8ad6\u4e32\u5167\u5bb9\uff0c\u548c\u5404\u4f4d\u5206\u4eab\u3002<\/p>\n<h3>\u4f60\u66ffcontrollers\u5beb\u6e2c\u8a66\u55ce\uff1f<\/h3>\n<blockquote><p>\n\u55e8\uff01<br \/>\n\u6211\u6700\u8fd1\u5728\u5b78Domain Driven Design\u8ddf\u5e7e\u500b\u6e2c\u8a66\u6280\u5de7\u3002<br \/>\n\u6211\u66ffrepositories\u3001services\u3001entities\u3001factories\u5beb\u5404\u7a2e\u6e2c\u8a66\u4f86\u78ba\u4fdd\u7a0b\u5f0f\u54c1\u8cea\u3002<br \/>\n\u56e0\u70ba\u7db2\u7ad9\u8d8a\u4f86\u8d8a\u4f9d\u8cf4\u524d\u7aef\u6280\u8853\u8ddfAJAX\uff0c\u6211\u89ba\u5f97\u4e5f\u66ffcontrollers\u5beb\u6e2c\u8a66\u6bd4\u8f03\u597d\u3002<br \/>\n\u4f46\u6211\u53c8\u89ba\u5f97\u6e2c\u8a66controllers\u6839\u672c\u6c92\u5f9econtrollers\u985e\u5225\u88e1\u9762initiate\u6216\u662fcall\u4efb\u4f55\u6771\u897f\u3002<br \/>\n\u53ea\u662f\u5beb\u4e00\u500b\u6e2c\u8a66\u53bb\u6a21\u64ec\u5c0d\u61c9controller action\u7684\u5167\u5bb9\u800c\u5df2\u3002<br \/>\n\u9019\u4e5f\u80fd\u53eb\u6e2c\u8a66\uff1f\u9019\u80fd\u5e36\u4f86\u591a\u5c11\u597d\u8655\uff1f<br \/>\n\u4f60\u5011\u66ffcontrollers\u5beb\u6e2c\u8a66\u55ce\uff1f<br \/>\n\u8ab0\u80fd\u6307\u9ede\u8ff7\u6d25\uff1f<br \/>\n\u611f\u6fc0\u4e0d\u76e1\uff01\n<\/p><\/blockquote>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/dohpaz42\">dohpaz42<\/a><\/h3>\n<p>\u8edf\u9ad4\u6e2c\u8a66\u662f\u4e00\u500b\u8d85\u8907\u96dc\u5927\u602a\u7378\u3002\u6bcf\u500b\u4eba\u6e2c\u8a66\u7684\u65b9\u6cd5\u90fd\u5dee\u5f88\u591a\u3002\u96fb\u8166\u7406\u8ad6\u90fd\u9019\u6a23\u7684\u3002<br \/>\n\u5148\u7c21\u55ae\u56de\u7b54\u4f60\u7684\u554f\u984c\uff1a\u6e2c\u8a66\u6240\u6709\u6771\u897f\u3002<br \/>\n\u5b8c\u6574\u56de\u7b54\u6bd4\u8f03\u8907\u96dc\u3002\u6211\u8a66\u8457\u6574\u7406\u51fa\u4f86\uff0c\u8acb\u898b\u8ad2\u3002<br \/>\n\u6e2c\u8a66\u6709\u5206\u5f88\u591a\u7a2e\uff1aunit tests\uff0cintegration test\uff0cregression tests\uff0cfeature tests\uff0c\u7b49\u7b49\u3002\u6839\u64da\u6e2c\u8a66\u7684\u5c0d\u8c61\u4e0d\u540c\uff0c\u4f60\u6703\u4f7f\u7528\u4e0d\u540c\u7684\u6e2c\u8a66\u3002<br \/>\n\u8209\u4f8b\u4f86\u8aaa\uff0c\u7576\u4f60\u5beb\u4e00\u500bmodel\uff0c\u57fa\u672c\u4e0a\u4f60\u6703\u7528\u5230unit test\u3002\u4f60\u6703\u66ff\u6bcf\u500bmethod\u53ef\u80fd\u8d70\u904e\u7684\u6bcf\u689d\u9053\u8def\u90fd\u5beb\u6e2c\u8a66\u3002<br \/>\n\u5982\u679c\u4f60\u5c0d\u9019\u89c0\u5ff5\u4e0d\u719f\uff0c\u5c31\u9019\u6a23\u60f3\uff1a\u82e5\u4f60\u7684method\u6703\u88fd\u9020X\u7a2e\u526f\u4f5c\u7528\uff0c\u90a3\u4f60\u5c31\u5f97\u66ff\u9019X\u7a2e\u526f\u4f5c\u7528\u5beb\u6e2c\u8a66\u3002\u6b63\u78ba\u57f7\u884c\u3001\u5931\u6557\u57f7\u884c\u7684\u60c5\u6cc1\u90fd\u7b97\u3002<br \/>\nunit test\u7684\u5b9a\u7fa9\u5c31\u662f\u6e2c\u8a66application\u4e2d\u7684\u4e00\u500b\u7279\u5b9a\u55ae\u4f4d\u3002\u5982\u679c\u6e2c\u8a66\u78b0\u5230\u5176\u4ed6models\u6216\u7a0b\u5f0f\u78bc\uff0c\u4f60\u5c31mock\u90a3\u4e9b\u7269\u4ef6\uff08\u5229\u7528dependency injection\uff09\u4f86\u5ffd\u7565\u5b83\u5011\u3002\u82e5\u6709\u500bdatabase model\u6703\u5beb\u5165\u8cc7\u6599\u5230database\uff0c\u6211\u9700\u8981\u53bb\u7ba1database layer\u662f\u5426\u7522\u51fa\u6b63\u78ba\u7684output\u55ce\uff1f\u5927\u6982\u4e0d\u9700\u8981\u3002\u4f60\u53ea\u9700\u8981\u6ce8\u610f\u6b63\u5728\u6e2c\u8a66\u7684\u90e8\u4efd\u7cfb\u7d71(System Under Test, SUT)\u6703\u91dd\u5c0ddatabase\u7684output\u6b63\u78ba\u53cd\u61c9\u3002\u9019\u4ee3\u8868\u4f60\u61c9\u8a72\u8981\u5c0d\u6240\u6709\u6703\u5f71\u97ff\u7cfb\u7d71\u904b\u4f5c\u7684database output\u5beb\u6e2c\u8a66\u3002\u53ea\u9700\u8981pass-through\u3001proxy\uff08\u6216\u662f\u4efb\u4f55\u4e0d\u6703\u9020\u6210\u526f\u4f5c\u7528\u7684\u65b9\u5f0f\uff09\u9019\u6a23\u7684\u7c21\u55ae\u65b9\u6cd5\uff0c\u5c31\u80fd\u6709\u6548\u5ffd\u7565\u90a3\u4e9bcode\u7684\u53ef\u80fd\u8d70\u6cd5\u3002\u8b6c\u5982\u8aaa\uff1a<\/p>\n<pre>\r\nfunction fooProxy() {\r\n    return $this->getDatabase()->query('SELECT * FROM foo');\r\n}\r\n<\/pre>\n<p>\u9019\u500bmethod\u53ea\u662f\u628aapplication\u4e2d\u53e6\u4e00\u5c64layer\u7684\u7d50\u679c\u4e1f\u56de\u53bb\uff0c\u800c\u90a3\u5c64layer\u4f60\u61c9\u8a72\u65e9\u5c31\u6e2c\u904e\u4e86\u3002\u6240\u4ee5\u4f60\u4e0d\u9700\u8981\u66fffooProxy\u5beb\u6e2c\u8a66\u3002<\/p>\n<p>\u5f88\u597d\uff0c\u611f\u8b1d\u4f60\u4fdd\u6301\u8010\u5fc3\u3002\u6b63\u5f0f\u56de\u7b54\u524d\uff0c\u6211\u60f3\u5148\u91d0\u6e05\u9019\u4e9b\u3002<\/p>\n<p>controllers\u901a\u5e38\u6703\u8207application\u4e2d\u7684\u591a\u500b\u90e8\u4efd\u4e00\u8d77\u5408\u4f5c\u3002\u5b83\u5011\u900f\u904efront controller\u3001routing\u6a5f\u5236\u4f86\u904b\u4f5c\uff0c\u4e26\u7528\u4e0amodels\u3001views\u3002\u5f88\u5c11\u6703\u6709\u5c08\u5c6c\u65bccontroller\u7684logic\uff08\u9019\u4e0d\u4ee3\u8868controllers\u4e0d\u9700\u8981\u6e2c\u8a66\uff09\u3002\u8981\u662f\u6709\u7684\u8a71\uff0c\u90a3\u5c31\u6e2c\u5427\u3002\u6e2c\u8a66\u5728\u4e0d\u540c\u72c0\u6cc1\u4e0b\uff0c\u4ed6\u5011\u8207\u5176\u4ed6code\u7684\u4e92\u52d5\u662f\u5426\u4e00\u5982\u9810\u671f\u3002\u9019\u9700\u8981integration tests\u624d\u884c\u3002<\/p>\n<p>\u4e00\u500bintegration test\u4e0d\u600e\u9ebc\u5728\u4e4e\u7cfb\u7d71\u904b\u4f5c\u7684\u7d30\u7bc0\u3002\u5b83\u53ea\u5728\u4e4e\uff1a\u5982\u679cXYZ\u653e\u9032\u53bb\uff0c\u90a3\u9ebc\u7d50\u679c\u61c9\u8a72\u8981\u662fZYX\u3002\u62ffREST API\u8209\u4f8b\uff1a\u5982\u679c\u6211\u4e1f\u4e00\u500bGET request\u5230\/resource\/a\uff0c\u6211\u61c9\u8a72\u5f97\u5230\u4e00\u500b200 response\uff0c\u4e5f\u8a31\u518d\u52a0\u4e0abody content\u3002\u9019\u6a23\u7b97\u901a\u904e\u6e2c\u8a66\u3002\u5982\u679c\u6211\u628a\u5e7e\u500b\u8b8a\u6578\u8ddfrequest\u4e00\u8d77\u4e1f\u51fa\u53bb\uff0c\u7d50\u679c\u5f97\u5230400 response\uff0c\u90a3\u5c31\u6c92\u901a\u904e\u6e2c\u8a66\u3002\u4e0d\u5728\u4e4e\u7cfb\u7d71\u662f\u5426\u78b0\u5230\u8cc7\u6599\u5eab\u9084\u662f\u6a94\u6848\u4ec0\u9ebc\u7684\uff0c\u53ea\u5728\u4e4eresponse\u662f\u4e0d\u662f200\u3002<\/p>\n<p>\u6b63\u5f0f\u56de\u7b54\u4f60\u7684\u554f\u984c\uff1a\u5c0d\uff0c\u6240\u6709\u6771\u897f\u90fd\u61c9\u8a72\u6e2c\u8a66\u3002\u4f60\u6703\u5beb\u5f88\u591a\u4e0d\u540c\u7684test\u3002\u9019\u53ef\u80fd\u5f88\u7463\u788e\uff0c\u751a\u81f3\u5f88\u7121\u804a\u3002\u4f46\u662f\u7576\u6240\u6709\u7684tests\u90fd\u901a\u904e\u7684\u6642\u5019\uff0c\u4f60\u7d42\u7a76\u6703\u7406\u89e3\u50f9\u503c\u4f55\u5728\u3002<\/p>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/whtevn\">whtevn<\/a><\/h3>\n<p>\u6211\u5011\u6703\u66ffcontrollers\u5beb\u6e2c\u8a66\u3002\u4f46\u662fintegration tests\uff0c\u4e0d\u662funit tests\u3002\u6211\u5011\u6703\u6e2c\u6240\u6709api endpoints\uff0c\u7136\u5f8c\u8a18\u4e0b\u4ed6\u5011\u5728\u7279\u5b9ainput\u4e0b\u7684\u5404\u7a2eresponse\u3002\u6211\u5011\u4e5f\u6703\u6e2c\u8cc7\u6599\u5eab\u662f\u5426\u6b63\u78ba\u66f4\u65b0\u3002\u9019\u6a23\u7b97\u662f\u9a57\u8b49\u4e86\u6574\u500bapplication\u3002<br \/>\n\u9019\u8ddfunit test\u8981\u6c42\u5fb9\u5e95\u7368\u7acb\u7684\u6982\u5ff5\u4e0d\u540c\u3002\u5f9eTDD\u7684\u89c0\u9ede\uff0c\u5148\u5bebunit tests\u662f\u5f88\u5408\u7406\u3002\u4f46\u662f\u5148\u5bebintegration tests\u66f4\u68d2\u3002\u5b83\u80fd\u5354\u52a9\u4f60\u5f04\u6e05\u695a\u529f\u80fd\uff0c\u53c8\u4e0d\u9700\u8981\u50cfunit test\u90a3\u6a23\u6709\u500b\u9010\u6f38\u905e\u589e\u7684feedback loop\u3002<\/p>\n<p>\u6211\u5011\u516c\u53f8\u5f37\u8abfintegration tests\u800c\u975eunit test\u3002\u6211\u5011\u898f\u5b9a\u6bcf\u500bendpoint\u90fd\u5fc5\u9808\u8981\u6709integration test\uff0c\u800cunit tests\u7531\u5de5\u7a0b\u5e2b\u81ea\u884c\u6c7a\u5b9a\u3002\u78ba\u8a8d\u7279\u5b9a\u7684method\u904b\u4f5c\u6b63\u5e38\u7576\u7136\u91cd\u8981\u3002\u4f46\u6211\u500b\u4eba\u8a8d\u70ba\uff0c\u78ba\u8a8d\u6240\u6709API endpoint\u90fd\u7b26\u5408\u898f\u683c\uff0c\u66f4\u91cd\u8981\u3002<\/p>\n<p>\u6211\u5beb\u4e86\u4e00\u500bintegration test\u5957\u4ef6\u3002\u6211\u653e\u5728github\u4e0a\uff0c\u4f46\u76ee\u524d\u53ea\u6709\u5e7e\u500b\u7bc4\u4f8b\u6709\u5beb\u8aaa\u660e\u3002<\/p>\n<p>https:\/\/github.com\/whtevn\/routest\/blob\/master\/sample\/tests\/succeed\/routest-test.js<\/p>\n<p>\u4e5f\u653e\u5728npm\u4e0a\u4e86\u3002\u5b83\u4e0d\u662f\u5916\u639b\uff0c\u53ea\u662f\u4e00\u500b\u514d\u8cbb\u5de5\u5177\u3002\u597d\u50cf\u4e5f\u7b97\u5916\u639b\u3002\u96a8\u4fbf\u5566\u3002<\/p>\n<p>https:\/\/www.npmjs.com\/package\/routest<\/p>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/mrargh\">mrargh<\/a><\/h3>\n<p>\u6211\u5011\u901a\u5e38\u5c0dmodels\u5bebunit test\uff0c\u7136\u5f8c\u7528web driver integration tests \u6e2ccontrollers \u2013 \u9019\u6a23\u5c31\u90fd\u6e2c\u5230\u4e86\u3002<\/p>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/philsturgeon\">philsturgeon<\/a><\/h3>\n<p>\u6c92\u4e8b\u5e79\u624d\u5beb\u5427&#8230;\u4f46\u6211\u7e3d\u662f\u6709\u4e8b\u5e79\u3002<br \/>\n\u6211\u5c0dlibraries\uff0cmodels\u4e4b\u985e\u7684\u5bebunit test\uff0c\u7136\u5f8c\u7528integration test\u6e2c\u5176\u9918\u90e8\u4efd\u3002<br \/>\n\u5c0dcontrollers\u5bebunit test\u4e0d\u662f\u58de\u4e8b\uff0c\u4f46\u4e0d\u9700\u8981\u512a\u5148\u8003\u616e\u3002<\/p>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/WishCow\">WishCow<\/a><\/h3>\n<p>\u6211\u4e0d\u5beb\u3002\u6211\u76e1\u91cf\u5728controllers\u5167\u5c11\u653elogic\uff0c\u628a\u4e8b\u60c5\u90fd\u4ea4\u7d66services\u8ddfmodels\u3002\u66ffcontrollers\u5beb\u6e2c\u8a66\u53ea\u662f\u518d\u6a21\u64ec\u4e00\u6b21\u4ed6\u5011\u7684\u4e92\u52d5\u800c\u5df2\u3002\u90a3\u6a23\u662f\u53ef\u4ee5\u9a57\u8b49\u53c3\u6578\u8207\u7d50\u679c\u7684\u95dc\u4fc2\uff0c\u4f46\u5c0d\u6211\u4f86\u8aaa\u592a\u82b1\u529f\u592b\u4e86\uff0c\u4e0d\u5212\u7b97\u3002<\/p>\n<h3><a href=\"http:\/\/www.reddit.com\/user\/chadcf\">chadcf<\/a><\/h3>\n<p>\u5982\u679c\u662fAPI application\u6211\u624d\u66ffcontrollers\u5beb\u6e2c\u8a66\u3002\u90a3\u4e9b\u7b97functional tests\u3002web\u4ecb\u9762\u7684\u8a71\uff0c\u6211\u6703\u8b93controllers\u5167\u5bb9\u5c11\u5230\u4e0d\u9700\u8981\u5bebtests\u3002\u5927\u539f\u5247\u662f\u8b93controller action\u5167\u5bb9\u5c11\u523010\u884c\u4ee5\u5167\uff0c\u53ea\u8b93\u5b83\u5efa\u7acb\u7269\u4ef6\u3001\u547c\u53eb\u7269\u4ef6\u65b9\u6cd5\uff0c\u7136\u5f8c\u56de\u50b3response\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u524d\u9663\u5b50\u770b\u5230\u66ffcontrollers\u5beb\u6e2c\u8a66\u7684\u6587\u7ae0\u3002\u6478\u4e86\u4e00\u9663\u5b50\u9084\u662f\u6c92\u638c\u63e1\u3002 \u8dd1\u53bbReddit\u767c\u554f\uff0c\u5f97\u5230\u597d\u591a\u56de\u61c9\u3002  &hellip; <a href=\"https:\/\/blog.turn.tw\/?p=1691\" class=\"more-link\">\u7e7c\u7e8c\u95b1\u8b80 <span class=\"screen-reader-text\">\u4f60\u66ffcontrollers\u5beb\u6e2c\u8a66\u55ce\uff1f<\/span> <span class=\"meta-nav\">&rarr;<\/span> <\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[2,4],"tags":[32],"_links":{"self":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1691"}],"collection":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1691"}],"version-history":[{"count":41,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1691\/revisions"}],"predecessor-version":[{"id":1739,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=\/wp\/v2\/posts\/1691\/revisions\/1739"}],"wp:attachment":[{"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.turn.tw\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}