{"id":12030,"date":"2017-02-28T00:00:00","date_gmt":"2017-02-27T16:00:00","guid":{"rendered":"https:\/\/fgchen.com\/wpedu2\/2017\/02\/28\/%e3%80%90%e8%bb%9f%e9%ab%94%e6%b8%ac%e8%a9%a6%e3%80%91visual-studio%e7%9a%84%e5%96%ae%e5%85%83%e6%b8%ac%e8%a9%a6%e5%af%a6%e4%bd%9c%e7%af%84%e4%be%8b\/"},"modified":"2026-03-30T14:51:31","modified_gmt":"2026-03-30T06:51:31","slug":"%e3%80%90%e8%bb%9f%e9%ab%94%e6%b8%ac%e8%a9%a6%e3%80%91visual-studio%e7%9a%84%e5%96%ae%e5%85%83%e6%b8%ac%e8%a9%a6%e5%af%a6%e4%bd%9c%e7%af%84%e4%be%8b","status":"publish","type":"post","link":"https:\/\/fgchen.com\/wpedu\/2017\/02\/%e3%80%90%e8%bb%9f%e9%ab%94%e6%b8%ac%e8%a9%a6%e3%80%91visual-studio%e7%9a%84%e5%96%ae%e5%85%83%e6%b8%ac%e8%a9%a6%e5%af%a6%e4%bd%9c%e7%af%84%e4%be%8b\/","title":{"rendered":"\u3010\u8edf\u9ad4\u6e2c\u8a66\u3011Visual Studio\u7684\u55ae\u5143\u6e2c\u8a66\u5be6\u4f5c\u7bc4\u4f8b"},"content":{"rendered":"\u8a3b\uff1a\u672c\u6587\u8cc7\u6599\u53c3\u8003\u81ea<a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms182532.aspx#BKMK_Prepare_the_walkthrough\">Walkthrough: Creating and Running Unit Tests for Managed Code<\/a>\uff0c\u63d0\u4f9b\u7d66\u8ab2\u5802\u4e0a\u5b78\u751f\u5b78\u7fd2\u53c3\u8003\u3002\n\u672c\u6559\u5b78\u7684\u76ee\u7684\u5728\u5b78\u7fd2\u5982\u4f55\u4f7f\u7528Visual Studio\u4f86\u64b0\u5beb\u55ae\u5143\u6e2c\u8a66\u7a0b\u5f0f\uff0c\u4e26\u900f\u904e\u6e2c\u8a66\u7684\u57f7\u884c\u767c\u73fe\u7a0b\u5f0f\u554f\u984c\u53ca\u4fee\u6b63\u554f\u984c\u3002\u53e6\u5916\uff0c\u6211\u5011\u4e5f\u5c07\u6703\u5b78\u7fd2\u5982\u4f55\u6e2c\u8a66\u7a0b\u5f0f\u55ae\u5143\u662f\u5426\u5b8c\u6574\u5730\u8655\u7406\u932f\u8aa4\u8207\u7a0b\u5f0f\u57f7\u884c\u6642\u671f\u7684\u4f8b\u5916\uff0c\u4ee5\u78ba\u4fdd\u7a0b\u5f0f\u55ae\u5143\u7684\u54c1\u8cea\u3002\n<img fetchpriority=\"high\" decoding=\"async\" class=\"size-full wp-image-4521 alignnone\" src=\"http:\/\/www.fgchen.com\/wp\/wp-content\/uploads\/2017\/02\/VS-TestExplorer.png\" alt=\"\" width=\"509\" height=\"577\" \/>\n\u5728\u9019\u500b\u6e2c\u8a66\u5be6\u4f5c\u4e2d\uff0c\u5fc5\u9808\u5b8c\u6210\u5e95\u4e0b\u5e7e\u4ef6\u5de5\u4f5c\uff1a\n1\uff0e<a href=\"#Bank\">\u5efa\u7acbBank\u5c08\u6848<\/a>\n2\uff0e<a href=\"#testProject\">\u5efa\u7acb\u55ae\u5143\u6e2c\u8a66\u5c08\u6848<\/a>\n3\uff0e<a href=\"#testClass\">\u5efa\u7acb\u6e2c\u8a66\u985e\u5225<\/a>\n4\uff0e<a href=\"#firstTestMethod\">\u5efa\u7acb\u7b2c1\u500b\u6e2c\u8a66\u65b9\u6cd5<\/a>\n5\uff0e<a href=\"#buildRun\">\u5efa\u7f6e\u548c\u57f7\u884c\u6e2c\u8a66<\/a>\n6\uff0e<a href=\"#fixCode\">\u4fee\u6b63\u4f60\u7684\u7a0b\u5f0f\u4e26\u91cd\u65b0\u57f7\u884c\u7a0b\u5f0f\u6e2c\u8a66<\/a>\n7\uff0e<a href=\"#improveCode\">\u4f7f\u7528\u55ae\u5143\u6e2c\u8a66\u4f86\u6539\u5584\/\u6539\u9032\u4f60\u7684\u7a0b\u5f0f\u78bc<\/a>\n\n<hr \/>\n\n<p id=\"Bank\"><strong><span style=\"font-size: 18pt\">\u3010\u5efa\u7acbBank\u5c08\u6848\u3011<\/span><\/strong><\/p>\n\n1\uff0e\u6253\u958bVisual Studio\n2\uff0e\u958b\u555f\u6a94\u6848-&gt;\u65b0\u589e-&gt;\u5c08\u6848\n3\uff0e\u65b0\u589e\u5c08\u6848\u7684\u5c0d\u8a71\u65b9\u584a\u6703\u986f\u793a\n4\uff0e\u5728\u5b89\u88dd\u7684\u7bc4\u672c\u4e2d\uff0c\u9078\u64c7Visual C#\n5\uff0e\u9078\u64c7\u985e\u5225\u5eab\n6\uff0e\u5c08\u6848\u540d\u7a31\u8a2d\u70baBank\uff0c\u9ede\u64ca\u78ba\u5b9a\u3002\n<div class=\"su-note\"  style=\"border-color:#810a00;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\"><div class=\"su-note-inner su-u-clearfix su-u-trim\" style=\"background-color:#9b2415;border-color:#fff0e1;color:#ffffff;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\">\u82e5\u76ee\u9304\u4e2d\u5df2\u7d93\u6709Bank\u5c08\u6848\uff0c\u8acb\u8b8a\u66f4\u540d\u7a31\u6216\u6539\u8b8a\u76ee\u9304\u3002<\/div><\/div>\n\u6b64\u6642\uff0c\u4e00\u500b\u65b0\u7684Bank\u5c08\u6848\u88ab\u5efa\u7acb\uff0c\u4e26\u4e14\u986f\u793a\u5728\u65b9\u6848\u7e3d\u7ba1\u8996\u7a97\u4e0a\uff0c\u5c08\u6848\u5167\u6709\u4e00\u500bClass1.cs\u6a94\uff0c\u88ab\u81ea\u52d5\u5730\u958b\u555f\u5728\u7a0b\u5f0f\u78bc\u7de8\u8f2f\u8996\u7a97\u4e0a\u3002\n<div class=\"su-note\"  style=\"border-color:#810a00;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\"><div class=\"su-note-inner su-u-clearfix su-u-trim\" style=\"background-color:#9b2415;border-color:#fff0e1;color:#ffffff;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\">\u82e5Class1.cs\u6a94\u6848\u6c92\u6709\u81ea\u52d5\u5730\u958b\u555f\uff0c\u5247\u5728\u65b9\u6848\u7e3d\u7ba1\u4e0a\u96d9\u64caClass1.cs\u6a94\u6848\u4f86\u958b\u555f\u3002<\/div><\/div>\n6\uff0e\u5f9e<a href=\"#BankSource\">\u5c08\u6848\u7bc4\u4f8b<\/a>\u8907\u88fd\u539f\u59cb\u78bc\n7\uff0e\u5c07Class1.cs\u6a94\u6848\u7684\u5167\u5bb9\u63db\u6210\u525b\u525b\u8907\u88fd\u7684\u539f\u59cb\u78bc\n8\uff0e\u5c07\u6a94\u6848\u5b58\u6210BankAccount.cs\n9\uff0e\u5f9e\u5efa\u7f6e\u9078\u55ae\uff0c\u6309\u5efa\u7f6e\u65b9\u6848\u529f\u80fd\n\u73fe\u5728\uff0c\u4f60\u5df2\u7d93\u5efa\u7f6e\u4e86Bank\u5c08\u6848\uff0c\u5176\u4e2d\u5305\u542b\u4e86\u6211\u5011\u6240\u8981\u9032\u884c\u6e2c\u8a66\u7684\u539f\u59cb\u78bc\uff0c\u4ee5\u53ca\u6240\u8981\u4f7f\u7528\u7684\u6e2c\u8a66\u5de5\u5177\u3002Bank\u7684\u547d\u540d\u7a7a\u9593, BankAccountNS, \u5305\u542b\u4e86\u516c\u7528\u985e\u5225BankAccount, \u5176\u4e2d\u88cf\u9762\u7684\u65b9\u6cd5\u662f\u4f60\u5728\u5e95\u4e0b\u7684\u7a0b\u5e8f\u8981\u9032\u884c\u6e2c\u8a66\u7684\u3002\n\u5728\u9019\u500b\u5feb\u901f\u7684\u5be6\u4f5c\u4e2d\uff0c\u6211\u5011\u5c08\u6ce8\u5728Debit\u65b9\u6cd5(\u5e33\u6236\u7684\u63d0\u6b3e)\uff0c\u7576\u5e33\u6236\u88cf\u7684\u9322\u88ab\u53d6\u51fa\u6642\uff0c\u501f\u652f\u65b9\u6cd5Debit\u6703\u88ab\u53eb\u7528\uff0c\u5e95\u4e0b\u662f\u7a0b\u5f0f\u78bc\uff1a\n\n<h3 id=\"BankSource\">\u5c08\u6848\u7bc4\u4f8b<\/h3>\n\n<pre class=\"theme:vs2012 lang:c# decode:true \">using System;\nnamespace BankAccountNS\n{\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Bank Account demo class.\n    \/\/\/ &lt;\/summary&gt;\n    public class BankAccount\n    {\n        private string m_customerName; \/\/\u5e33\u6236\u540d\u7a31\n        private double m_balance;\/\/\u5e33\u6236\u9918\u984d\n        private bool m_frozen = false; \/\/\u5e33\u6236\u662f\u5426\u51cd\u7d50\u5e03\u6797\u8b8a\u6578\n        private BankAccount() \/\/\u5efa\u69cb\u5b50\n        {\n        }\n        public BankAccount(string customerName, double balance) \/\/\u5efa\u69cb\u5b50\uff0c\u50b3\u5165\u65b0\u589e\u5e33\u6236\u540d\u7a31\u548c\u6de8\u984d\/\u9918\u984d(\u958b\u6236\u91d1\u984d)\n        {\n            m_customerName = customerName; \/\/\u65b0\u589eBankAccount\u7269\u4ef6\u4e2d\u7684\u5e33\u6236\u540d\u7a31\u8a2d\u70ba\u50b3\u5165\u7684\u540d\u7a31\n            m_balance = balance;\/\/\u65b0\u589eBankAccount\u7269\u4ef6\u4e2d\u7684\u6de8\u503c\u8a2d\u70ba\u50b3\u5165\u7684\u6de8\u503c\n        }\n        public string CustomerName \/\/\u6210\u54e1\u8b8a\u6578 m_customerName\u7684\u5b58\u53d6\u65b9\u6cd5\n        {\n            get { return m_customerName; }\n        }\n        public double Balance \/\/\u5e33\u6236\u6de8\u503c\u7684\u5b58\u53d6\u65b9\u6cd5\n        {\n            get { return m_balance; }\n        }\n        public void Debit(double amount) \/\/\u5f9e\u5e33\u6236\u4e2d\u501f\u652f\uff0c\u91d1\u984d\u662f\u7531amount\u6c7a\u5b9a\n        {\n            if (m_frozen) \/\/\u82e5\u5e33\u6236\u51cd\u7d50\u4e86\n            {\n                throw new Exception(\"Account frozen\"); \/\/\u7a0b\u5f0f\u4e1f\u51fa\u4e00\u500b\u4f8b\u5916\uff0c\u4f8b\u5916\u5b57\u4e32\u70baAccount frozen\uff0c\u610f\u601d\u662f\u5e33\u6236\u51cd\u7d50\n            }\n            if (amount &gt; m_balance) \/\/\u82e5\u8981\u501f\u652f\u7684\u91d1\u984d\u5927\u65bc\u5e33\u6236\u7684\u6de8\u984d\n            {\n                throw new ArgumentOutOfRangeException(\"amount\"); \/\/\u7a0b\u5f0f\u4e1f\u51fa\u4e00\u500bArgumentOutOfRangeException\u4f8b\u5916\uff0c\u4f8b\u5916\u5b57\u4e32\u70baamount\n            }\n            \/\/\u8a3b\uff1aArgumentOutOfRangeException\u7684\u610f\u601d\u662f\u53c3\u6578\u8d85\u51fa\u7bc4\u570d\u7684\u4f8b\u5916\n            if (amount &lt; 0) \/\/\u82e5\u8981\u501f\u652f\u7684\u91d1\u984d\u662f\u8ca0\u503c\n            {\n                throw new ArgumentOutOfRangeException(\"amount\"); \/\/\u7a0b\u5f0f\u4e1f\u51fa\u4e00\u500bArgumentOutOfRangeException\u4f8b\u5916\uff0c\u4f8b\u5916\u5b57\u4e32\u70baamount\n            }\n            m_balance += amount; \/\/ intentionally incorrect code   \u4e00\u958b\u59cb\u9019\u500b\u7a0b\u5f0f\u662f\u932f\u8aa4\u7684\uff0c\u501f\u652f\u5f8c\u6de8\u503c\u61c9\u8a72\u662f\u6e1b\u53bbamount\uff0c\u4e0d\u662f\u52a0\u4e0aamount\uff0c\u9019\u662f\u70ba\u4e86\u7b49\u6703\u5152\u6e2c\u8a66\u7522\u751f\u932f\u8aa4\u2026\n        }\n        public void Credit(double amount) \/\/\u8cb8\u6b3e\n        {\n            if (m_frozen) \/\/\u82e5\u5e33\u6236\u51cd\u7d50\u4e86\n            {\n                throw new Exception(\"Account frozen\");\n            }\n            if (amount &lt; 0) \/\/\u82e5\u8981\u8cb8\u6b3e\u7684\u91d1\u984d\u662f\u8ca0\u503c\n            {\n                throw new ArgumentOutOfRangeException(\"amount\");\n            }\n            m_balance += amount; \/\/\u5e33\u6236\u6de8\u984d\u52a0\u4e0a\u8cb8\u5165\u7684\u91d1\u984d\n        }\n        private void FreezeAccount() \/\/\u51cd\u7d50\u5e33\u6236\u65b9\u6cd5\n        {\n            m_frozen = true;\n        }\n        private void UnfreezeAccount() \/\/\u89e3\u9664\u5e33\u6236\u7684\u51cd\u7d50\n        {\n            m_frozen = false;\n        }\n        public static void Main() \/\/\u4e3b\u7a0b\u5f0f\uff0c\u7a0b\u5f0f\u7684\u9032\u5165\u9ede\n        {\n            BankAccount ba = new BankAccount(\"Mr. Bryan Walton\", 11.99); \/\/\u65b0\u589e\u4e00\u500bBankAccount\u7269\u4ef6\uff0c\u5e33\u6236\u540d\u7a31\u662fMr. Bryan Walton\uff0c\u958b\u555f\u91d1\u984d\u662f11.99\n            ba.Credit(5.77); \/\/\u5b58\u6b3e5.77\n            ba.Debit(11.22); \/\/\u53d6\u6b3e11.22\n            Console.WriteLine(\"Current balance is ${0}\", ba.Balance); \/\/\u5370\u51fa\u5e33\u6236\u6de8\u984d\n        }\n    }\n}\n<\/pre>\n\n<hr \/>\n\n<h3 id=\"testProject\">\u3010\u5efa\u7acb\u55ae\u5143\u6e2c\u8a66\u5c08\u6848\u3011<\/h3>\n\n1\uff0e\u5f9e\u6a94\u6848\u9078\u55ae\uff0c\u9078\u64c7\u52a0\u5165\uff0c\u518d\u9078\u64c7\u65b0\u5c08\u6848\u3002\n2\uff0e\u5728\u65b0\u589e\u5c08\u6848\u7684\u5c0d\u8a71\u65b9\u584a\u4e2d\uff0c\u9078\u64c7Visual C#\u88cf\u7684\u6e2c\u8a66\u3002\n3\uff0e\u9078\u64c7\u55ae\u5143\u6e2c\u8a66\u5c08\u6848\n4\uff0e\u5728\u540d\u7a31\u7684\u65b9\u584a\u4e2d\uff0c\u8f38\u5165BankTest\uff0c\u9078\u64c7\u78ba\u5b9a\u3002\n5\uff0e\u5b8c\u6210\u4e0a\u9762\u7684\u6b65\u9a5f\u5f8c\uff0cBankTests\u5c08\u6848\u6703\u52a0\u5230Bank\u65b9\u6848\u4e2d\u3002\n6\uff0e\u5728BankTests\u5c08\u6848\u4e2d\uff0c\u52a0\u5165Bank\u5c08\u6848\u5230\u53c3\u8003\u4e2d\u3002\n<img decoding=\"async\" class=\"alignnone size-full wp-image-4528\" src=\"http:\/\/www.fgchen.com\/wp\/wp-content\/uploads\/2017\/02\/VS-UnitTest-AddBank2Ref.png\" alt=\"\" width=\"275\" height=\"472\" \/>\n\n<hr \/>\n\n<h3 id=\"testClass\">\u3010\u5efa\u7acb\u6e2c\u8a66\u985e\u5225\u3011<\/h3>\n\n\u6211\u5011\u9700\u8981\u4e00\u500b\u6e2c\u8a66\u985e\u5225\u4f86\u9a57\u8b49BankAccount\u985e\u5225\uff0c\u6211\u5011\u53ef\u4ee5\u4f7f\u7528\u7531\u5c08\u6848\u7bc4\u672c\u7522\u751f\u7684UnitTest1.cs\uff0c\u53ea\u662f\u6211\u5011\u61c9\u8a72\u7d66\u4e88\u8a72\u6a94\u6848\u4e00\u500b\u66f4\u6709\u610f\u7fa9\u7684\u540d\u7a31\uff0c\u8b8a\u66f4\u540d\u7a31\u53ea\u9700\u8981\u5728\u65b9\u6848\u7e3d\u7ba1\u4e2d\u4e00\u500b\u6b65\u9a5f\u5c31\u5b8c\u6210\u3002\n<strong>\u8b8a\u66f4\u985e\u5225\u6a94\u7684\u540d\u7a31<\/strong>\n\u5728\u65b9\u6848\u7e3d\u7ba1\u4e2d\uff0c\u9078\u64c7\u5728BankTests\u5c08\u6848\u4e2d\u7684UnitTest1.cs \u6a94\u6848\uff0c\u66f4\u540d\u7684\u65b9\u5f0f\u53ef\u4ee5\u76f4\u63a5\u9ede\u64ca\u6a94\u6848\u4f86\u66f4\u540d\uff0c\u6216\u5728\u6a94\u6848\u4e0a\u6309\u53f3\u9375\u9078\u53d6\u91cd\u65b0\u547d\u540d\u4f86\u66f4\u540d\uff0c\u6b64\u6642\u6211\u5011\u5c07\u6a94\u6848\u91cd\u65b0\u547d\u540d\u70baBankAccountTests.cs\uff0c\u539f\u59cb\u78bc\u5217\u8868\u5982\u4e0b\uff1a\n\n<pre class=\"theme:vs2012 lang:c# decode:true \">\/\/ unit test code\nusing System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nnamespace BankTests\n{\n    [TestClass]\n    public class BankAccountTests\n    {\n        [TestMethod]\n        public void TestMethod1()\n        {\n        }\n    }\n}\n<\/pre>\n\n\u5728\u4e0a\u9762\u6a94\u6848using\u8655\uff0c\u52a0\u5165\u5e95\u4e0b\u9019\u884c\uff0c\u5ba3\u544a\u4f7f\u7528BankAccountNS\u547d\u540d\u7a7a\u9593\uff0c\u4ee5\u4fbf\u547c\u53eb\u8a72\u547d\u540d\u7a7a\u9593\u4e0b\u7684\u5c08\u6848\u3002\n\n<pre class=\"theme:vs2012 width-set:true width-mode:2 width:300 striped:false nums:false lang:c# decode:true \">using BankAccountNS;\n<\/pre>\n\n<strong>\u6e2c\u8a66\u985e\u5225\u9700\u6c42<\/strong>\n\u4e00\u500b\u6e2c\u8a66\u985e\u5225\u7684\u6700\u5c0f\u9700\u6c42\u70ba\uff1a\n\uff0e\u5728\u5fae\u8edfMicrosoft\u55ae\u5143\u6e2c\u8a66\u6846\u67b6\u4e4b\u4e0b\uff0c\u4efb\u4f55\u985e\u5225\u82e5\u5305\u542b\u8981\u5728\u6e2c\u8a66\u7e3d\u7ba1\u4e0b\u57f7\u884c\u7684\u55ae\u5143\u6e2c\u8a66\u65b9\u6cd5\uff0c\u5fc5\u9808\u52a0\u4e0a[TestClass] \u5c6c\u6027\u3002\n\uff0e\u6bcf\u4e00\u500b\u7531\u6e2c\u8a66\u7e3d\u7ba1\u57f7\u884c\u7684\u6e2c\u8a66\u65b9\u6cd5\u5fc5\u9808\u5728\u958b\u982d\u52a0\u4e0a[TestMethod]\u5c6c\u6027\u3002\n\u5728\u4e00\u500b\u6e2c\u8a66\u5c08\u6848\u4e0b\uff0c\u53ef\u4ee5\u6709\u5176\u4ed6\u672a\u52a0\u4e0a[TestClass] \u5c6c\u6027\u7684\u985e\u5225\uff0c\u4e5f\u53ef\u4ee5\u5728\u6e2c\u8a66\u985e\u5225\u4e0b\uff0c\u672a\u52a0\u4e0a [TestMethod] \u5c6c\u6027\u7684\u65b9\u6cd5\uff0c\u4f60\u53ef\u4ee5\u5728\u4f60\u7684\u6e2c\u8a66\u65b9\u6cd5\u4e2d\uff0c\u4f7f\u7528\u9019\u4e9b\u985e\u5225\u8207\u65b9\u6cd5\u3002\n\n<hr \/>\n\n<h3><a id=\"firstTestMethod\"><\/a>\u3010\u5efa\u7acb\u7b2c\u4e00\u500b\u6e2c\u8a66\u65b9\u6cd5\u3011<\/h3>\n\n\u63a5\u4e0b\u4f86\uff0c\u6211\u5011\u8981\u5beb\u6e2c\u8a66\u65b9\u6cd5\u4f86\u6e2c\u8a66 BankAccount\u985e\u5225\u88cfDebit\u65b9\u6cd5\u7684\u884c\u70ba\uff0c\u85c9\u7531\u5206\u6790\u8a72\u65b9\u6cd5\uff0c\u6211\u5011\u6c7a\u5b9a\u81f3\u5c11\u6709\u4e09\u500b\u884c\u70ba\u5fc5\u9808\u8981\u6aa2\u67e5\uff1a\n\n<p style=\"padding-left: 60px\">1\uff0e\u5982\u679c\u501f\u652f\u91d1\u984d\u5927\u65bc\u5e33\u6236\u6de8\u984d\uff0c\u8a72\u65b9\u6cd5\u4e1f\u51fa\u4e00\u500bArgumentOutOfRangeException\u4f8b\u5916\u3002<\/p>\n\n<p style=\"padding-left: 60px\">2\uff0e\u5982\u679c\u501f\u652f\u91d1\u984d\u5c0f\u65bc\u96f6\uff0c\u8a72\u65b9\u6cd5\u4e1f\u51fa\u4e00\u500bArgumentOutOfRangeException\u4f8b\u5916\u3002<\/p>\n\n<p style=\"padding-left: 60px\">3\uff0e\u5982\u679c\u57281.) and 2.) \u4e2d\u7684\u6aa2\u67e5\u901a\u904e\uff0c\u8a72\u65b9\u6cd5\u5247\u5c07\u5e33\u6236\u6de8\u984d\u6e1b\u53bb\u501f\u652f\u91d1\u984d\u3002<\/p>\n\n\u5728\u6211\u5011\u7b2c\u4e00\u500b\u6e2c\u8a66\u4e2d\uff0c\u6211\u5011\u9a57\u8b49\u4e00\u500b\u6709\u6548\u7684\u91d1\u984d(\u5c0f\u65bc\u5e33\u6236\u6de8\u984d\uff0c\u4e26\u4e14\u5927\u65bc\u96f6\u7684\u6578\u5b57)\uff0c\u8b93\u6211\u5011\u5f9e\u5e33\u6236\u4e2d\u53d6\u51fa\u6b63\u78ba\u7684\u91d1\u984d\u3002\n<strong><span style=\"font-size: 14pt\">\u5efa\u7acb\u4e00\u500b\u6e2c\u8a66\u65b9\u6cd5<\/span><\/strong>\n\u52a0\u5165&#8221;using BankAccountNS;&#8221;\u6558\u8ff0\u5230BankAccountTests.cs\u6a94\u6848\u4e2d\u3002\n\u52a0\u5165\u4e0b\u5217\u65b9\u6cd5\u5230BankAccountTests\u985e\u5225:\n\n<p id=\"BankAccountTests\"><span style=\"font-size: 18pt\"><strong>BankAccountTests.cs<\/strong><\/span><\/p>\n\n<pre class=\"theme:vs2012 lang:c# decode:true \">using System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing BankAccountNS;\nnamespace BankTest\n{\n    [TestClass]\n    public class BankAccountTests\n    {\n        [TestMethod]\n        public void Debit_WithValidAmount_UpdatesBalance()\n        {\n            \/\/ arrange\n            double beginningBalance = 11.99; \/\/\u958b\u6236\u7684\u91d1\u984d\n            double debitAmount = 4.55; \/\/\u501f\u652f\u7684\u91d1\u984d\n            double expected = 7.44; \/\/\u9810\u671f\u501f\u652f\u5f8c\uff0c\u5e33\u6236\u7684\u9810\u671f\u91d1\u984d\n            BankAccount account = new BankAccount(\"Mr. Bryan Walton\", beginningBalance);\n            \/\/\u5efa\u7acbBankAccount\u7269\u4ef6\uff0c\u540d\u70baaccount\uff0c\u4e0a\u9762\u7684\u5efa\u69cb\u5b50\u7528\u4f86\u6307\u6b63\u540d\u7a31\u53ca\u958b\u6236\u7684\u91d1\u984d\n            \/\/ account\u7269\u4ef6\u501f\u652fdebitAmount\u91d1\u984d\n            account.Debit(debitAmount);\n            \/\/ assert\uff0c\u65b7\u8a00\uff0c\u65b7\u8a00\u7a0b\u5f0f\u4e0d\u6703\u767c\u751f\u6307\u5b9a\u7684\u689d\u4ef6\uff0c\u82e5\u767c\u751f\uff0c\u5247\u7522\u751f\u65b7\u8a00\u4f8b\u5916\uff0c\u65b7\u8a00\u662f\u53ef\u4ee5\u5728\u7a0b\u5f0f\u6b63\u5f0f\u7248\u4e2d\u95dc\u6389\u7684\u3002\n            \/\/\u65b7\u8a00\u7528\u4f86\u6e2c\u8a66\u5047\u8a2d\u689d\u4ef6\u662f\u5426\u6210\u7acb\uff0c\u7576\u6e2c\u8a66\u5b8c\u90fd\u7121\u554f\u984c\u5f8c\uff0c\u91cb\u51fa\u6b63\u5f0f\u7248\u672c\u5f8c\uff0c\u65b7\u8a00\u6a5f\u5236\u5c31\u9700\u8981\u95dc\u9589\uff0c\u4ee5\u63d0\u5347\u7a0b\u5f0f\u7684\u6548\u80fd\u3002\n            double actual = account.Balance;\n            Assert.AreEqual(expected, actual, 0.001, \"Account not debited correctly\");\n            \/\/\u65b7\u8a00expected\u8207actual\u5169\u500b\u96d9\u7cbe\u5ea6\u6d6e\u9ede\u6578\u503c\u76f8\u7b49\uff0c\u6216\u5728\u5728\u6307\u5b9a\u7cbe\u78ba\u5ea6\u5167(0.001)\uff0c\u82e5\u65b7\u8a00\u5931\u6557\uff0c\u5247\u4e1f\u51fa\u4e00\u500bAssertError\u3002\n        }\n    }\n}\n<\/pre>\n\n&nbsp;\n\u9019\u500b\u65b9\u6cd5\u76f8\u7576\u7c21\u55ae\uff0c\u6211\u5011\u8a2d\u7f6e\u4e86\u4e00\u500b\u65b0\u7684BankAccount\u7269\u4ef6\uff0c\u5167\u542b\u521d\u59cb\u7684\u6de8\u984d\uff0c\u7136\u5f8c\u5f9e\u4e2d\u53d6\u51fa\u4e00\u500b\u6709\u6548\u7684\u501f\u652f\u91d1\u984d\u3002\u6211\u5011\u4f7f\u7528\u5fae\u8edf\u55ae\u5143\u6e2c\u8a66\u6846\u67b6\u4e2d\u7684 AreEqual \u65b9\u6cd5\u4f86\u9a57\u8b49\u6700\u5f8c\u7684\u6de8\u984d\u662f\u5426\u70ba\u6211\u5011\u6240\u9810\u671f\u7684\u3002\n<strong><span style=\"font-size: 14pt\">\u6e2c\u8a66\u65b9\u6cd5\u898f\u7bc4<\/span><\/strong>\n\u4e00\u500b\u6e2c\u8a66\u65b9\u6cd5\u5fc5\u9808\u6eff\u8db3\u4e0b\u5217\u898f\u7bc4\uff1a\n1\uff0e\u65b9\u6cd5\u5fc5\u9808\u52a0\u4e0a\u51a0\u4e0a [TestMethod]\u5c6c\u6027\u3002\n2\uff0e\u65b9\u6cd5\u4e0d\u56de\u50b3\u503c\uff0c\u4e5f\u5c31\u662f\u5fc5\u5ba3\u544a\u70bavoid\u3002\n3\uff0e\u65b9\u6cd5\u4e0d\u53ef\u4ee5\u5e36\u5165\u53c3\u6578\u3002\n\n<hr \/>\n\n<h3><a id=\"buildRun\"><\/a>\u3010\u5efa\u7f6e\u548c\u57f7\u884c\u6e2c\u8a66\u3011<\/h3>\n\n1\uff0e\u9078\u64c7\u5efa\u7f6e\u9078\u55ae\uff0c\u5efa\u7f6e\u65b9\u6848\u3002\u82e5\u6c92\u6709\u932f\u8aa4\u767c\u751f\uff0c\u6e2c\u8a66\u7e3d\u7ba1\u8996\u7a97\u6703\u51fa\u73feDebit_WithValidAmount_UpdatesBalance \u5728\u672a\u57f7\u884c\u500b\u6e2c\u8a66\u6e05\u55ae\u4e0a\uff0c\u5982\u679c\u6e2c\u8a66\u7e3d\u7ba1\u6c92\u6709\u51fa\u73fe\uff0c\u90a3\u9ebc\u9078\u64c7\u6e2c\u8a66\u9078\u55ae\uff0c\u9078\u64c7\u8996\u7a97\uff0c\u9078\u64c7\u6e2c\u8a66\u7e3d\u7ba1\uff0c\u90a3\u9ebc\u6e2c\u8a66\u7e3d\u7ba1\u8996\u7a97\u5c31\u6703\u51fa\u73fe\u4e86\u3002\n2\uff0e\u5728\u6e2c\u8a66\u7e3d\u7ba1\u8996\u7a97\u4e2d\uff0c\u9ede\u9078\u5168\u90e8\u57f7\u884c(\u6216\u5f9e\u6e2c\u8a66\u9078\u55ae-&gt;\u57f7\u884c-&gt;\u6240\u6709\u6e2c\u8a66)\uff0c\u6e2c\u8a66\u57f7\u884c\u6642\uff0c\u6e2c\u8a66\u7e3d\u7ba1\u4e0a\u65b9\u7684\u72c0\u614b\u689d\u6703\u5448\u73fe\u52d5\u614b\u8b8a\u5316\uff0c\u7576\u6e2c\u8a66\u57f7\u884c\u5b8c\u7562\u5f8c\uff0c\u82e5\u6240\u6709\u7684\u6e2c\u8a66\u90fd\u901a\u904e\u4e86\uff0c\u90a3\u9ebc\u8a72\u72c0\u614b\u689d\u6703\u5448\u73fe\u7da0\u8272\uff0c\u82e5\u6e2c\u8a66\u5931\u6557\uff0c\u8a72\u72c0\u614b\u689d\u6703\u5448\u73fe\u7d05\u8272\u3002\u6211\u5011\u9019\u500b\u6e2c\u8a66\u662f\u5931\u6557\u7684\uff0c\u6240\u4ee5\u4f60\u6703\u770b\u5230\u7d05\u8272\u7684\u72c0\u614b\u689d\uff0c\u6e2c\u8a66\u7e3d\u7ba1\u4e2d\u53ef\u4ee5\u6aa2\u8996\u6e2c\u8a66\u7684\u7d30\u7bc0\u3002\n\n<hr \/>\n\n<a id=\"fixCode\"><\/a><strong><span style=\"font-size: 14pt\">\u4fee\u6b63\u4f60\u7684\u7a0b\u5f0f\u78bc\u4e26\u91cd\u65b0\u57f7\u884c\u4f60\u7684\u6e2c\u8a66<\/span><\/strong>\n\u9996\u5148\uff0c\u6211\u5011\u5206\u6790\u57f7\u884c\u6e2c\u8a66\u5f8c\u7684\u7d50\u679c\uff0c\u6e2c\u8a66\u7d50\u679c\u5305\u542b\u4e86\u4e00\u500bAssert.AreEquals \u5931\u6557\u8a0a\u606f\uff1a\n<div class=\"su-note\"  style=\"border-color:#e1e1e1;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\"><div class=\"su-note-inner su-u-clearfix su-u-trim\" style=\"background-color:#fbfbfb;border-color:#ffffff;color:#000000;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\">\n\u6e2c\u8a66\u540d\u7a31: Debit_WithValidAmount_UpdatesBalance \u6e2c\u8a66 FullName: BankTest.BankAccountTests.Debit_WithValidAmount_UpdatesBalance \u6e2c\u8a66\nBankAccountTests.cs : \u884c 12\n\u6e2c\u8a66\u5931\u6557 \u00a0Debit_WithValidAmount_UpdatesBalance\n\u8a0a\u606f: Assert.AreEqual \u5931\u6557\u3002\u9810\u671f\u503c &lt;7.44&gt; \u548c\u5be6\u969b\u503c &lt;16.54&gt; \u4e4b\u9593\u7684\u9810\u671f\u5dee\u7570\u6c92\u6709\u5927\u65bc &lt;0.001&gt;\u3002\n<\/div><\/div>\n\u4e0a\u9762\u7684\u6e2c\u8a66\u5931\u6557\u8a0a\u606f\u986f\u793a\u57f7\u884c\u501f\u652f\u65b9\u6cd5\u5f8c\uff0c\u9810\u671f\u7684\u5e33\u6236\u6de8\u984d\u4e0d\u662f\u6240\u9810\u671f\u7684\uff0c\u56e0\u6b64\uff0c\u6211\u5011\u6aa2\u67e5Debit\u65b9\u6cd5\u90a3\u500b\u74b0\u7bc0\u51fa\u932f\u4e86\u3002\u6211\u5011\u767c\u73fe\uff0c\u7a0b\u5f0f\u78bc\u986f\u793a\u501f\u652f\u65b9\u6cd5\u662f\u5c07\u5e33\u6236\u6de8\u984d\u52a0\u4e0a\u501f\u652f\u91d1\u984d\uff0c\u800c\u6b63\u78ba\u7684\u65b9\u6cd5\u662f\u7a0b\u5f0f\u78bc\u61c9\u8a72\u5c07\u5e33\u6236\u6de8\u984d\u6e1b\u53bb\u501f\u652f\u91d1\u984d\uff01\n\n<hr \/>\n\n<strong><span style=\"font-size: 14pt\">\u4fee\u6b63\u7a0b\u5f0f\u932f\u8aa4<\/span><\/strong>\n\u70ba\u4e86\u4fee\u6b63\u4e0a\u9762\u7684\u932f\u8aa4\uff0c\u6211\u5011\u53ea\u8981\u628a\u539f\u672c\u7684\u7a0b\u5f0f\u6558\u8ff0\nm_balance += amount;\n\u63db\u6210\uff1a\nm_balance -= amount;\n<strong><span style=\"font-size: 14pt\">\u518d\u6b21\u5730\u57f7\u884c\u6e2c\u8a66<\/span><\/strong>\n\u5728\u6e2c\u8a66\u7e3d\u7ba1\u4e2d\uff0c\u9078\u64c7\u5168\u90e8\u57f7\u884c\uff0c\u57f7\u884c\u6e2c\u8a66\u5b8c\u7562\u4e4b\u5f8c\uff0c\u6211\u5011\u767c\u73fe\u6e2c\u8a66\u7e3d\u7ba1\u7684\u72c0\u614b\u689d\u5448\u73fe\u7da0\u8272\u4e86\uff0c\u8868\u793a\u901a\u904e\u4e86\u7a0b\u5f0f\u7684\u6e2c\u8a66\uff01\u6211\u5011\u770b\u5230\u4e86\u5e95\u4e0b\u7684\u8a0a\u606f\uff1a\n\u6e2c\u8a66\u6210\u529f &#8211; Debit_WithValidAmount_UpdatesBalance\n\n<hr \/>\n\n<span style=\"font-size: 18pt\"><strong><a id=\"improveCode\"><\/a>\u3010\u4f7f\u7528\u55ae\u5143\u6e2c\u8a66\u4f86\u6539\u5584\/\u6539\u9032\u4f60\u7684\u7a0b\u5f0f\u78bc\u3011<\/strong><\/span>\nThis section describes how an iterative process of analysis, unit test development, and refactoring can help you make your production code more robust and effective.\n\u9019\u500b\u7ae0\u7bc0\u63cf\u8ff0\u4e86\u4e00\u500b\u5206\u6790\u3001\u55ae\u5143\u6e2c\u8a66\u767c\u5c55\u3001\u548c\u91cd\u69cb\u7684\u8fed\u4ee3\u904e\u7a0b\u662f\u5982\u4f55\u5730\u5354\u52a9\u4f60\u4f86\u7522\u751f\u66f4\u5f37\u56fa\u53ca\u6709\u6548\u7684\u7522\u54c1\u7a0b\u5f0f\u78bc\u3002\n<strong>Analyze the issues \u5206\u6790\u554f\u984c<\/strong>\nAfter creating a test method to confirm that a valid amount is correctly deducted in the <code>Debit<\/code> method, we can turn to remaining cases in our original analysis: \u00a0\u5728\u5efa\u7acb\u4e86\u4e00\u500b\u6e2c\u8a66\u65b9\u6cd5\u4f86\u78ba\u8a8d\u5728Debit\u65b9\u6cd5\u4e2d\u7684\u6709\u6548\u6263\u6e1b\u984d\u4e4b\u5f8c\uff0c\u6211\u5011\u7dca\u63a5\u8457\u8655\u7406\u5728\u6211\u5011\u4e00\u958b\u59cb\u7684\u5206\u6790\u4e2d\u7684\u5176\u4ed6\u72c0\u6cc1\uff1a\n\n<ol>\n    <li>The method throws an <code>ArgumentOutOfRangeException<\/code> if the debit amount is greater than the balance.\n\u5982\u679cdebit\u5927\u65bcbalance\u6642\uff0c\u65b9\u6cd5\u5c07\u6703\u4e1f\u51fa<code>ArgumentOutOfRangeException\u4f8b\u5916<\/code><\/li>\n    <li>It also throws <code>ArgumentOutOfRangeException<\/code> if the debit amount is less than zero.\n\u5982\u679cdebit\u5c0f\u65bc\u96f6\u7684\u8a71\uff0c\u65b9\u6cd5\u4e5f\u5c07\u6703\u4e1f\u51fa<code>ArgumentOutOfRangeException\u4f8b\u5916\u3002<\/code><\/li>\n<\/ol>\n\n<strong>Create the test methods \u5efa\u7acb\u6e2c\u8a66\u65b9\u6cd5<\/strong>\nA first attempt at creating a test method to address these issues seems promising:\n\u7b2c\u4e00\u6b21\u5617\u8a66\u5efa\u7acb\u4e00\u500b\u6e2c\u8a66\u65b9\u6cd5\u4f86\u5b9a\u4f4d\u51fa\u4e0a\u9762\u8a0e\u8ad6\u7684\u554f\u984c\n\n<pre class=\"theme:vs2012 lang:c# decode:true\">\/\/unit test method\n[TestMethod]\n[ExpectedException(typeof(ArgumentOutOfRangeException))]\npublic void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()\n{\n    \/\/ arrange\n    double beginningBalance = 11.99;\n    double debitAmount = -100.00;\n    BankAccount account = new BankAccount(\"Mr. Bryan Walton\", beginningBalance);\n    \/\/ act\n    account.Debit(debitAmount);\n    \/\/ assert is handled by ExpectedException\n}<\/pre>\n\nWe use the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute.aspx\">ExpectedExceptionAttribute<\/a> attribute to assert that the right exception has been thrown. \u6211\u5011\u4f7f\u7528<a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute.aspx\">ExpectedExceptionAttribute<\/a>\u00a0\u5c6c\u6027\u4f86\u65b7\u5b9a\u6b63\u78ba\u7684\u4f8b\u5916\u5df2\u7d93\u88ab\u4e1f\u51fa\u4f86\u3002The attribute causes the test to fail unless an <code>ArgumentOutOfRangeException<\/code> is thrown. Running the test with both positive and negative <code>debitAmount<\/code> values and then temporarily modifying the method under test to throw a generic <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.applicationexception.aspx\">ApplicationException<\/a> when the amount is less than zero demonstrates that test behaves correctly. \u00a0\u9664\u975e\u4e00\u500b<code>ArgumentOutOfRangeException<\/code>\u00a0\u88ab\u62cb\u64f2\u51fa\u4f86\uff0c\u5426\u5247\u8a72\u5c6c\u6027\u662f\u5c0e\u81f4\u6e2c\u8a66\u7684\u5931\u6557\uff0cTo test the case when the amount withdrawn is greater than the balance, all we need to do is: \u70ba\u4e86\u6e2c\u8a66withdrawn\u9019\u500b\u63d0\u6b3e\u7684\u6578\u503c\u5927\u65bcbalance\u9918\u984d\u9019\u500b\u6848\u4f8b\uff0c\u6211\u5011\u6240\u8981\u505a\u7684\u4e8b\u60c5\u6709\uff1a\n\n<ol>\n    <li>\u5efa\u7acb\u4e00\u500b\u65b0\u7684\u6e2c\u8a66\u65b9\u6cd5\uff0c\u4e26\u547d\u540d\u70ba\uff1a<code>Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange<\/code>.<\/li>\n    <li>\u5f9e<code>Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange\u9019\u500b\u65b9\u6cd5\u8907\u88fd\u7a0b\u5f0f\u78bc\u5230\u65b0\u7684\u65b9\u6cd5\u3002<\/code><\/li>\n    <li>\u8a2d\u5b9a<code>debitAmount\u6578\u503c\u5927\u65bc<\/code>balance\u9918\u984d\u3002<\/li>\n<\/ol>\n\n<strong>Run the tests \u57f7\u884c\u6e2c\u8a66<\/strong>\nRunning the two methods with different values for <code>debitAmount<\/code> demonstrates that the tests adequately handle our remaining cases. Running all three tests confirm that all cases in our original analysis are correctly covered. \u5206\u5225\u4ee5\u4e0d\u540c\u7684debitAmount\u6578\u503c\u4f86\u57f7\u884c\u90192\u500b\u65b9\u6cd5\u4f86<span style=\"font-family: monospace\">\u6aa2\u8996\u90192\u500b\u6e2c\u8a66\u662f\u5426\u8655\u7f6e\u5f8c\u7e8c\u7684\u6848\u4f8b\uff0c\u57f7\u884c\u6240\u67093\u500b\u6e2c\u8a66\u4f86\u78ba\u8a8d\u5728\u6211\u5011\u539f\u59cb\u5206\u6790\u4e2d\u7684\u6240\u6709\u6848\u4f8b\u662f\u5426\u6b63\u78ba\u5730\u6db5\u84cb\u3002<\/span>\n<strong>Continue the analysis \u9032\u4e00\u6b65\u5730\u5206\u6790<\/strong>\nHowever, the last two test methods are also somewhat troubling. We cannot be certain which condition in the code under test throws when either test runs. Some way of differentiating the two conditions would be helpful. As we think about the problem more, it becomes apparent that knowing which condition was violated would increase our confidence in the tests. This information would also very likely be helpful to the production mechanism that handles the exception when it is thrown by the method under test. Generating more information when the method throws would assist all concerned, but the <code>ExpectedException<\/code> attribute cannot supply this information..\nLooking at the method under test again, we see both conditional statements use an <code>ArgumentOutOfRangeException<\/code> constructor that takes name of the argument as a parameter:\n\n<pre class=\"theme:vs2012 lang:c# decode:true\">throw new ArgumentOutOfRangeException(\"amount\");\n<\/pre>\n\nFrom a search of the MSDN Library, we discover that a constructor exists that reports far richer information. <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/dsyf5bb2.aspx\">ArgumentOutOfRangeException<\/a><code>(String, Object, String)<\/code> includes the name of the argument, the argument value, and a user-defined message. We can refactor the method under test to use this constructor. Even better, we can use publicly available type members to specify the errors.\n<strong>Refactor the code under test<\/strong>\nWe first define two constants for the error messages at class scope:\n\n<pre class=\"theme:vs2012 lang:c# decode:true\">\/\/ class under test\npublic const string DebitAmountExceedsBalanceMessage = \"Debit amount exceeds balance\";\npublic const string DebitAmountLessThanZeroMessage = \"Debit amount less than zero\";<\/pre>\n\nWe then modify the two conditional statements in the <code>Debit<\/code> method:\n\n<div id=\"code-snippet-10\" class=\"codeSnippetContainer\">\n<pre class=\"theme:vs2012 lang:c# decode:true\">\/\/ method under test\n\/\/ ...\n    if (amount &gt; m_balance)\n    {\n        throw new ArgumentOutOfRangeException(\"amount\", amount, DebitAmountExceedsBalanceMessage);\n    }\n    if (amount &lt; 0)\n    {\n        throw new ArgumentOutOfRangeException(\"amount\", amount, DebitAmountLessThanZeroMessage);\n    }\n\/\/ ...\n<\/pre>\n<strong>Refactor the test methods<\/strong>\nIn our test method, we first remove the <code>ExpectedException<\/code> attribute. In its place, we catch the thrown exception and verify that it was thrown in the correct condition statement. However, we must now decide between two options to verify our remaining conditions. For example in the <code>Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange<\/code> method, we can take one of the following actions:\n<ul>\n    <li>Assert that the <code>ActualValue<\/code> property of the exception (the second parameter of the <code>ArgumentOutOfRangeException<\/code> constructor) is greater than the beginning balance. This option requires that we test the <code>ActualValue<\/code> property of the exception against the <code>beginningBalance<\/code>variable of the test method, and also requires then verify that the <code>ActualValue<\/code> is greater than zero.<\/li>\n    <li>Assert that the message (the third parameter of the constructor) includes the <code>DebitAmountExceedsBalanceMessage<\/code> defined in the <code>BankAccount<\/code> class.<\/li>\n<\/ul>\nThe <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms243984.aspx\">StringAssert.Contains<\/a> method in the Microsoft unit test framework enables us to verify the second option without the calculations that are required of the first option.\nA second attempt at revising <code>Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange<\/code> might look like:\n<pre class=\"theme:vs2012 lang:c# decode:true\">[TestMethod]\npublic void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()\n{\n    \/\/ arrange\n    double beginningBalance = 11.99;\n    double debitAmount = 20.0;\n    BankAccount account = new BankAccount(\"Mr. Bryan Walton\", beginningBalance);\n    \/\/ act\n    try\n    {\n        account.Debit(debitAmount);\n    }\n    catch (ArgumentOutOfRangeException e)\n    {\n        \/\/ assert\n        StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);\n    }\n}\n<\/pre>\n<strong>Retest, rewrite, and reanalyze<\/strong>\nWhen we retest the test methods with different values, we encounter the following facts:\n<ol>\n    <li>If we catch the correct error by using an assert where <code>debitAmount<\/code> that is greater than the balance, the <code>Contains<\/code> assert passes, the exception is ignored, and so the test method passes. This is the behavior we want.<\/li>\n    <li>If we use a <code>debitAmount<\/code> that is less than 0, the assert fails because the wrong error message is returned. The assert also fails if we introduce a temporary <code>ArgumentOutOfRange<\/code> exception at another point in the method under test code path. This too is good.<\/li>\n    <li>If the <code>debitAmount<\/code> value is valid (i.e., less than the balance but greater than zero, no exception is caught, so the assert is never caught. The test method passes. This is not good, because we want the test method to fail if no exception is thrown.<\/li>\n<\/ol>\nThe third fact is a bug in our test method. To attempt to resolve the issue, we add a <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms243738.aspx\">Fail<\/a> assert at the end of the test method to handle the case where no exception is thrown.\nBut retesting shows that the test now fails if the correct exception is caught. The catch statement resets the exception and the method continues to execute, failing at the new assert. To resolve the new problem, we add a <code>return<\/code> statement after the <code>StringAssert<\/code>. Retesting confirms that we have fixed our problems. Our final version of the <code>Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange<\/code> looks like the following:\n<pre class=\"theme:vs2012 lang:c# decode:true\">[TestMethod]\npublic void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()\n{\n    \/\/ arrange\n    double beginningBalance = 11.99;\n    double debitAmount = 20.0;\n    BankAccount account = new BankAccount(\"Mr. Bryan Walton\", beginningBalance);\n    \/\/ act\n    try\n    {\n        account.Debit(debitAmount);\n    }\n    catch (ArgumentOutOfRangeException e)\n    {\n        \/\/ assert\n        StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);\n        return;\n    }\n    Assert.Fail(\"No exception was thrown.\");\n}\n<\/pre>\n<\/div>\n\n<div class=\"codeSnippetContainer\">\n<div id=\"content\" class=\"content\">\n<div>\n<div class=\"topic\">\n<div id=\"mainSection\">\n<div id=\"mainBody\">\nIn this final section, the work that we did improving our test code led to more robust and informative test methods. But more importantly, the extra analysis also led to better code in our project under test.\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"rightNavigationMenu\">\n<hr \/>\n&nbsp;\n<\/div>\n<\/div>\n\n&nbsp;","protected":false},"excerpt":{"rendered":"<p>\u8a3b\uff1a\u672c\u6587\u8cc7\u6599\u53c3\u8003\u81eaWalkthrough: Creating and Runni &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","fifu_image_url":"","fifu_image_alt":"","footnotes":""},"categories":[266],"tags":[],"class_list":["post-12030","post","type-post","status-publish","format-standard","hentry","category-266"],"_links":{"self":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12030","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/comments?post=12030"}],"version-history":[{"count":1,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12030\/revisions"}],"predecessor-version":[{"id":13602,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12030\/revisions\/13602"}],"wp:attachment":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/media?parent=12030"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/categories?post=12030"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/tags?post=12030"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}