<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_風語·深蓝</title><subtitle type="text">Agile Methodology, HeadStorm And MindMap, they will change me.</subtitle><id>http://feed.cnblogs.com/blog/u/10975/rss</id><updated>2012-05-03T16:25:33Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/10975/rss"/><entry><id>http://www.cnblogs.com/Xrinehart/archive/2012/05/03/2480977.html</id><title type="text">8天学通MongoDB（实际操作版）&amp;mdash;&amp;mdash;第一天 基础入门</title><summary type="text">为了方便练手，便跟着《8天学通MongoDB》系列实际操作，并做学习日记记录。 一：下载 到MongoDB官方下载页面，有32位和64位版本，需要注意两点： 根据业界规则，偶数为“稳定版”（如：1.6.X，1.8.X），奇数为“开发版”（如：1.7.X，1.9.X)，这两个版本的区别相信大家都知道吧。 32bit的mongodb最大只能存放2G的数据，64bit就没有限制。 我选择的是2.0...</summary><published>2012-05-03T09:45:00Z</published><updated>2012-05-03T09:45:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2012/05/03/2480977.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2012/05/03/2480977.html"/><content type="html">&lt;p&gt;为了方便练手，便跟着《&lt;a href="http://www.cnblogs.com/huangxincheng/category/355399.html" target="_blank"&gt;8天学通MongoDB&lt;/a&gt;》系列实际操作，并做学习日记记录。&lt;/p&gt; &lt;p&gt;一：下载&lt;/p&gt; &lt;p&gt;到&lt;a href="http://www.mongodb.org/downloads" target="_blank"&gt;MongoDB官方下载页面&lt;/a&gt;，有32位和64位版本，需要注意两点：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;根据业界规则，偶数为“稳定版”（如：1.6.X，1.8.X），奇数为“开发版”（如：1.7.X，1.9.X)，这两个版本的区别相信大家都知道吧。  &lt;li&gt;32bit的mongodb最大只能存放2G的数据，64bit就没有限制。 &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;我选择的是2.0.4的64位版。下载后解压到C盘，把文件夹名改为mongodb。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;二：启动&lt;/p&gt; &lt;ol&gt; &lt;li&gt;启动之前，在C:\mongodb目录下添加子目录db，用于存放mongodb的数据。  &lt;li&gt;在命令行中，输入以下代码  &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_900457" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_900457').style.display='none'; document.getElementById('Code_Open_Image_900457').style.display='inline'; document.getElementById('Code_Open_Text_900457').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_900457" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_900457').style.display='none'; getElementById('Code_Closed_Image_900457').style.display='inline'; getElementById('Code_Closed_Text_900457').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_900457" class="cnblogs_code_Collapse"&gt;代码&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_900457"&gt;&lt;br/&gt;cd C:\mongodb\bin&lt;br/&gt;mongod --dbpath=C:\mongodb\db&lt;/div&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201205/201205031844451068.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201205/201205031844472445.png" width="634" height="466"&gt;&lt;/a&gt;&lt;/span&gt; &lt;li&gt;最后要看下是否开启成功，从图中的信息中获知，mongodb采用27017端口，那么我们就在浏览器里面键入“&lt;a href="http://localhost:27017/" target="_blank"&gt;http://localhost:27017/&lt;/a&gt;”，打开后，mongodb告诉我们在27017上Add 1000可以用http模式查看mongodb的管理信息（即&lt;a href="http://localhost:28017/" target="_blank"&gt;http://localhost:28017/&lt;/a&gt;）&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;三：基本操作&lt;/p&gt;&lt;p&gt;接下来实践Mongodb的增删查改，mongod是MongoDB的服务器端启动程序，而mongo则是MongoDB的Shell，可以简单认为是客户端，同时也是个js的编译器，默认连接到test库。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_338270" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_338270').style.display='none'; document.getElementById('Code_Open_Image_338270').style.display='inline'; document.getElementById('Code_Open_Text_338270').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_338270" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_338270').style.display='none'; getElementById('Code_Closed_Image_338270').style.display='inline'; getElementById('Code_Closed_Text_338270').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_338270" class="cnblogs_code_Collapse"&gt;代码&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_338270"&gt;&lt;br/&gt;cd c:\mongodb\bin&lt;br/&gt;mongo&lt;/div&gt;&lt;/span&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201205/201205031844495434.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201205/201205031844515632.png" width="512" height="243"&gt;&lt;/a&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;insert操作&lt;br&gt;db.person.insert({"name":"jack", "age":20})&lt;br&gt;db.person.insert({"name":"joe", "age":25})&lt;br&gt;通过以上两行命令，插入了两行记录到集合person中。需要注意的是，若集合person若不存在，则会直接创建一个新的。&lt;/li&gt;&lt;li&gt;find操作&lt;br&gt;db.person.find()&lt;br&gt;db.person.find({"name":"joe"})&lt;br&gt;不带任何参数的find方法会列出该集合中所有的记录&lt;/li&gt;&lt;li&gt;update操作&lt;br&gt;db.person.update({"name":"joe"},{"name":"joe","age":35})&lt;br&gt;需要注意的是，update中的第二个参数，即目标值，必须是包含完整的字段，否则其他字段会变成Null值&lt;/li&gt;&lt;li&gt;remove操作&lt;br&gt;db.person.remove()&lt;br&gt;需要注意该操作若不带任何参数，会删除所有数据，且不可撤回。&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2480977.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2012/05/03/2480977.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/12/18/2292752.html</id><title type="text">构建Ubuntu Server试验环境</title><summary type="text">目标： 现在在Linux下有很多非常强大的开源服务器组件，比如Redis, Memcached, RabbitMQ, MongoDB, Hadoop等，非常值得研究，因此准备着手做一个Ubuntu的虚拟机，在里面安装这些服务，供外部调用测试。 第一步：使用Windows Server 2K8R2中的Hyper-V作为虚拟机宿主，因为我的电脑只有无线网卡，而Hyper-V默认不支持使用无线...</summary><published>2011-12-18T15:38:00Z</published><updated>2011-12-18T15:38:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/12/18/2292752.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/12/18/2292752.html"/><content type="html">&lt;p&gt;目标：&lt;/p&gt;  &lt;p&gt;现在在Linux下有很多非常强大的开源服务器组件，比如Redis, Memcached, RabbitMQ, MongoDB, Hadoop等，非常值得研究，因此准备着手做一个Ubuntu的虚拟机，在里面安装这些服务，供外部调用测试。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;第一步：使用Windows Server 2K8R2中的Hyper-V作为虚拟机宿主，因为我的电脑只有无线网卡，而Hyper-V默认不支持使用无线网卡作为虚拟机的网卡，因此需要做以下操作：&lt;/p&gt;  &lt;p&gt;首先，在Virtual Network Manger里面天机一个Internal Only类型的虚拟网络：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337224389.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb10" border="0" alt="image_thumb10" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/20111218233723933.png" width="740" height="697" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337255526.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb12" border="0" alt="image_thumb12" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337263706.png" width="740" height="697" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;然后，在Network Connections面板中，把无线网卡和新建的这个虚拟网络设备之间创建桥连接，创建好后如下图所示：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337277426.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb14" border="0" alt="image_thumb14" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337285922.png" width="707" height="312" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;这样就算是设置好虚拟机的网络了，接下来开始Ubuntu Server的安装。&lt;/p&gt;    &lt;p&gt;第二步，配置Ubuntu Server。&lt;/p&gt;  &lt;p&gt;1. 设置虚拟机名和存放位置&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337306054.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb16" border="0" alt="image_thumb16" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337317822.png" width="735" height="541" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;2. 设置虚拟机的内存大小&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337331924.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb18" border="0" alt="image_thumb18" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337342056.png" width="735" height="541" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;3. 设置虚拟机使用的网卡，设置为刚刚添加的Virtual Internet连接&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337352188.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb20" border="0" alt="image_thumb20" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337373957.png" width="735" height="541" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;4. 为虚拟机系统设置硬盘，Hyper-V缺省创建的虚拟硬盘会根据实际使用增大，不会一开始就划定设定的大小&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337384089.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb24" border="0" alt="image_thumb24" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337394221.png" width="735" height="541" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;5. 为虚拟机安装系统，挂接Ubuntu Server的安装盘，这里我选择的是ubuntu-11.10-server-amd64.iso，然后这就完成了虚拟机的基本配置。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/20111218233740765.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb26" border="0" alt="image_thumb26" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337428945.png" width="735" height="541" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;第三步，开始在虚拟机中安装Ubuntu Server了。&lt;/p&gt;  &lt;p&gt;1. 启动虚拟机，进入安装界面，首先是选择安装提示语言类型，为了更好的兼容性，请选择English&lt;/p&gt;  &lt;p&gt;2. 接下来，会出现具体的安装提示，直接按回车选择Install Ubuntu Server就行了&lt;/p&gt;  &lt;p&gt;3. 选择系统的缺省语言，还是选择English, 国家选择United States&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337431585.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb28" border="0" alt="image_thumb28" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337448129.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;4. 在Configure the network时，会提示无法找到网卡，先点继续安装&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337467182.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb30" border="0" alt="image_thumb30" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337477314.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;5. 为虚拟机的Ubuntu Server设置一个机器名：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337489082.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb33" border="0" alt="image_thumb33" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337497578.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;6. 设定时区，选择Asia里的Chongqing&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337519346.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb35" border="0" alt="image_thumb35" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337529478.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;7. 设定Ubuntu Server的磁盘分区，选择缺省的Guided – use entire disk and set up LVM即可，接下来几步都选Yes和缺省值即可&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337537658.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb37" border="0" alt="image_thumb37" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337547791.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;8. 完成基本的安装之后，安装向导会要求输入一个日常使用的帐号。（System Admin是用户名，sa是帐号ID，密码使用1234）&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337567923.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb39" border="0" alt="image_thumb39" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337578055.png" width="820" height="712" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;9. 接下来的Home目录是否加密选No, Http Proxy空着以及不自动更新升级&lt;/p&gt;  &lt;p&gt;10. 最后是附加软件安装选择，请勾上Open SSH Server, LAMP Server， 然后继续，等待片刻之后，完成Ubuntu Server的初步安装。&lt;/p&gt;    &lt;p&gt;第四步，配置Ubuntu Server环境。&lt;/p&gt;  &lt;p&gt;1. 首先要做的肯定是配置Ubuntu Server的IP地址和DNS&lt;/p&gt;  &lt;p&gt;Ubuntu的网络配置信息放在 /etc/network/interfaces 中，而DNS信息在/etc/resolv.conf中&lt;/p&gt;  &lt;p&gt;首先使用sudo –s切换为root用户，再使用nano /etc/network/interfaces和nano /etc/resolv.conf命令分别设置IP和DNS，最后使用/etc/init.d/networking restart应用设置的IP地址和DNS。&lt;/p&gt;  &lt;p&gt;可以使用ifconfig命令和ping命令来验证配置情况。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337588187.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb1" border="0" alt="image_thumb1" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182337594731.png" width="679" height="448" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;2. 因为此前在安装阶段已经选择了OpenSSH和LAMP，因此可以在宿主机器的浏览器中访问虚拟机里的Apache Server，验证是否工作正常；同时也可以使用pietty等SSH终端去连接虚拟机了&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182338006782.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb3" border="0" alt="image_thumb3" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182338011025.png" width="843" height="223" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;3. 执行sudo apt-get update，更新当前的Ubuntu Server，这可能会需要十几分钟&lt;/p&gt;  &lt;p&gt;4. 再执行sudo apt-get install build-essential安装基本的编译环境。&lt;/p&gt;  &lt;p&gt;5. 接下来我们就可以开始安装更多的Linux Service了。&lt;/p&gt;  &lt;p&gt;第五步，安装Redis&lt;/p&gt;  &lt;p&gt;1. 访问官方网站的下载页&lt;a href="http://redis.io/download"&gt;http://redis.io/download&lt;/a&gt; , 可以在下面的Installation节找到类似如下的安装命令示例，然后在Ubuntu Server环境中执行以下命令：&lt;/p&gt;  $ wget http://redis.googlecode.com/files/redis-2.4.4.tar.gz&lt;br/&gt;$ tar xzf redis-2.4.4.tar.gz&lt;br/&gt;$ cd redis-2.4.4&lt;br/&gt;$ make$ sudo make install&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;2. 接下来是配置init启动脚本，命令行如下：&lt;/p&gt;$ wget https://github.com/ijonas/dotfiles/raw/master/etc/init.d/redis-server&lt;br/&gt;$ sudo mv redis-server /etc/init.d/redis-server&lt;br/&gt;$ sudo mv redis.conf /etc/redis.conf$ sudo useradd redis$ sudo mkdir -p /var/lib/redis$ sudo mkdir -p /var/log/redis$ sudo chown redis.redis /var/lib/redis$ sudo chown redis.redis /var/log/redis&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;p&gt;3. 修改redis.conf文件，主要修改点节点是： &lt;/p&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;  #是否作为后台服务运行，默认为no  daemonize yes  #客户端空闲多久会超时，默认为0（即永不过期）  timeout 300  #设置日志文件位置，默认为运行目录的stdout文件  logfile /var/log/redis/redis.log  #设置数据文件存放目录，默认为./  dir /var/lib/redis&lt;/blockquote&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;   &lt;/blockquote&gt;&lt;p&gt;4. 把Redis设为开机启动，然后启动Redis，并进行验证 &lt;/p&gt;&lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;code&gt;sudo&lt;/code&gt; &lt;code&gt;update-rc.d redis-server defaults&lt;/code&gt;&lt;/p&gt;sudo /etc/init.d/redis-server start使用redis-cli连接服务器进行测试之后如下图所示：&lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182339588674.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image_thumb[2]" border="0" alt="image_thumb[2]" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201112/201112182339596613.png" width="679" height="448" /&gt;&lt;/a&gt;&lt;p&gt;（未完待续）&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2292752.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/12/18/2292752.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/07/11/2102669.html</id><title type="text">单元测试培训系列：（二）单元测试与Visual Studio</title><summary type="text">单元测试需要有一定的工具和框架的支撑，在早期，一般我们使用的都是NUnit这套单元测试框架进行。后来微软在Visual Studio中集成了单元测试功能后，提供了更为强劲的功能以及集成整合能力，就没有必要再继续使用Nunit了。这一章节，主要就是介绍Visual Stuido中常见的单元测试相关的Attribute的功能和使用场景。基本类AttributeTestClassAttribute用于标识包含测试方法的类。任何一个单元测试类，必须在类上添加该Attribute，否则不会被Visual Studio识别为单元测试类，里面的所有方法也无法正确识别；TestMethodAttribute用</summary><published>2011-07-10T16:16:00Z</published><updated>2011-07-10T16:16:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/11/2102669.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/11/2102669.html"/><content type="html">&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;/blockquote&gt;&lt;p&gt;单元测试需要有一定的工具和框架的支撑，在早期，一般我们使用的都是NUnit这套单元测试框架进行。后来微软在Visual Studio中集成了单元测试功能后，提供了更为强劲的功能以及集成整合能力，就没有必要再继续使用Nunit了。&lt;/p&gt;&lt;p&gt;这一章节，主要就是介绍Visual Stuido中常见的单元测试相关的Attribute的功能和使用场景。&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: large;" size="5"&gt;&lt;strong&gt;基本类Attribute&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;TestClassAttribute&lt;/p&gt;&lt;p&gt;用于标识包含测试方法的类。任何一个单元测试类，必须在类上添加该Attribute，否则不会被Visual Studio识别为单元测试类，里面的所有方法也无法正确识别；&lt;/p&gt;&lt;p&gt;TestMethodAttribute&lt;/p&gt;&lt;p&gt;用于标记测试方法。只有添加了该Attribute的方法才会被识别为单元测试方法，才会被添加到Test List中；&lt;/p&gt;&lt;p&gt;PriorityAttribute&lt;/p&gt;&lt;p&gt;用于标记单元测试方法的执行优先级，即先后顺序。（但我测试时候未能生效，别的朋友有成功过，具体原因不详）&lt;/p&gt;&lt;p&gt;DescriptionAttribute&lt;/p&gt;&lt;p&gt;标记并描述当前单元测试方法，会出现在Test List中（默认不显示这列，需要手动添加），方便在运行测试时，方便的了解测试方法的含义&lt;/p&gt;&lt;p&gt;TestPropertyAttribute&lt;/p&gt;&lt;p&gt;传递Key/Value元数据的一个Attribute，本身没什么特别意义，允许在一个方法上标记多个。&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: large;" size="5"&gt;&lt;strong&gt;初始化与清理类Attribute&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: small;" size="3"&gt;此类别的所有属性都必须写在静态方法上&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;AssemblyInitializeAttribute&lt;/p&gt;&lt;p&gt;当一个程序集中第一个单元测试方法被执行前会执行该方法，用于初始化操作&lt;/p&gt;&lt;p&gt;ClassInitializeAttribute&lt;/p&gt;&lt;p&gt;当第一次执行当前类中的单元测试方法前会执行该方法，用于初始化操作&lt;/p&gt;&lt;p&gt;TestInitializeAttribute&lt;/p&gt;&lt;p&gt;当前类中的每个单元测试方法都会先执行该方法&lt;/p&gt;&lt;p&gt;TestCleanupAttribute&lt;/p&gt;&lt;p&gt;当前类中每个单元测试方法后，都会执行该方法，用于卸载和清除&lt;/p&gt;&lt;p&gt;ClassCleanupAttribute&lt;/p&gt;&lt;p&gt;当执行完该类所有单元测试之后，执行该方法&lt;/p&gt;&lt;p&gt;AssemblyCleanup&lt;/p&gt;&lt;p&gt;当当前程序集中所有单元测试都执行完后，执行该方法&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large;" size="5"&gt;异常以及超时类Attribute&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;ExpectedExceptionAttribute&lt;/p&gt;&lt;p&gt;标记当前单元测试应该出现的异常，如果应该出现的指定类型的异常没有出现，则认为这个单元测试失败&lt;/p&gt;&lt;p&gt;TimeoutAttribute&lt;/p&gt;&lt;p&gt;设定当前单元测试的最大执行时间，如果该方法执行的时间超过了该值，则会认为该单元测试未能通过&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: large;" size="5"&gt;特殊作用类Attribute&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;DataSourceAttribute&lt;/p&gt;&lt;p&gt;逐行读取指定数据源中的数据，每行数据会执行当前单元测试方法一次。通过testContextInstance对象上的DataRow索引器属性访问每行上的各列数据。&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;" color="#ff0000"&gt;需要注意的是，以上绝大多数Attribute并不能使用在单元测试中，而是在集成测试中使用&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;" color="#ff0000"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;" color="#ff0000"&gt;以下附上一段典型的单元测试代码作为展示&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('0e2b7a87-5f66-4851-afce-6182a5896dd0')"&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_0e2b7a87-5f66-4851-afce-6182a5896dd0" /&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" class="code_img_opened" id="code_img_opened_0e2b7a87-5f66-4851-afce-6182a5896dd0" onclick="cnblogs_code_hide('0e2b7a87-5f66-4851-afce-6182a5896dd0',event)" style="display: none;" /&gt;&lt;div id="cnblogs_code_open_0e2b7a87-5f66-4851-afce-6182a5896dd0" class="cnblogs_code_hide"&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System.Data;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System.Reflection;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System.Text;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System.Collections.Generic;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; System.Linq;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; ALite.Core.UnitTestSimple;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt;&lt;span style="color: #000000;"&gt; Moq;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;namespace&lt;/span&gt;&lt;span style="color: #000000;"&gt; ALite.Core.Tests&lt;br /&gt;{&lt;br /&gt;    [TestClass]&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderUnitTest&lt;br /&gt;    {&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestContext _testContextInstance;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestContext TestContext&lt;br /&gt;        {&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;get&lt;/span&gt;&lt;span style="color: #000000;"&gt; { &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; _testContextInstance; }&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;set&lt;/span&gt;&lt;span style="color: #000000;"&gt; { _testContextInstance &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; value; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [TestMethod]&lt;br /&gt;        [Description(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;A normal unit testing for the method 'Submit.'&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestNormalSubmit()&lt;br /&gt;        {&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var mockPersistence &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;IPersistence&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;();&lt;br /&gt;            mockPersistence.Setup(e &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; e.Save(It.IsAny&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;Order&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;())).Returns(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Arrange&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var target &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Order&lt;br /&gt;                             {&lt;br /&gt;                                 PesistenceHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; mockPersistence.Object&lt;br /&gt;                             };&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;const&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; expected &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Action&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; result &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; target.Submit();&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Assert&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            Assert.AreEqual(expected, result);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [TestMethod]&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 是且只有是抛出DataException类型的异常时才会算通过，即便是子类也会判断为未通过&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;        [ExpectedException(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt;(DataException))]&lt;br /&gt;        [Description(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;A unit testing for the method 'Submit' with a DataException when using the property PersistenceHandler&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestSubmitWithException()&lt;br /&gt;        {&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var mockPersistence &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;IPersistence&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;();&lt;br /&gt;            mockPersistence.Setup(e &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; e.Save(It.IsAny&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;Order&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;())).Throws(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; DataException());&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Arrange&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var target &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Order&lt;br /&gt;            {&lt;br /&gt;                PesistenceHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; mockPersistence.Object&lt;br /&gt;            };&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;const&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; expected &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Action&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; result &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; target.Submit();&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Assert&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            Assert.AreEqual(expected, result);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [TestMethod]&lt;br /&gt;        [Description(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;举例说明如何测试私有方法和使用TestProperty属性&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        [TestProperty(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;City&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;深圳&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        [TestProperty(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Province&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;广东&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        [TestCategory(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Category1&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestCountTotalPrice()&lt;br /&gt;        {&lt;br /&gt;            var method &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; Util.GetCallingMethod(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;false&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;            var attributeType &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt;(TestPropertyAttribute);&lt;br /&gt;            var attributes &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; method.GetCustomAttributes(attributeType, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            var city &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; ((TestPropertyAttribute)attributes[&lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;]).Value;&lt;br /&gt;            var province &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; ((TestPropertyAttribute)attributes[&lt;/span&gt;&lt;span style="color: #800080;"&gt;1&lt;/span&gt;&lt;span style="color: #000000;"&gt;]).Value;&lt;br /&gt;&lt;br /&gt;            var address &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;.Format(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;{0} {1}&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, province, city);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var mockDeliveryManageHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;IDeliveryManage&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;();&lt;br /&gt;            mockDeliveryManageHandler.Setup(e &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; e.CountDeliveryFee(It.IsAny&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;())).Returns(&lt;/span&gt;&lt;span style="color: #800080;"&gt;10.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Arrange&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var orderDetails1 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;                                   {&lt;br /&gt;                                       TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;10.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;                                   };&lt;br /&gt;&lt;br /&gt;            var orderDetails2 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;                                    {&lt;br /&gt;                                        TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;20.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;                                    };&lt;br /&gt;&lt;br /&gt;            var orderDetails3 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;                                    {&lt;br /&gt;                                        TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;30.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;                                    };&lt;br /&gt;&lt;br /&gt;            var details &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; List&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;OrderDetail&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; {orderDetails1, orderDetails2, orderDetails3};&lt;br /&gt;&lt;br /&gt;            var target &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Order()&lt;br /&gt;                             {&lt;br /&gt;                                 Destination &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; address,&lt;br /&gt;                                 DeliveryManageHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; mockDeliveryManageHandler.Object,&lt;br /&gt;                                 OrderDetails &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; details&lt;br /&gt;                             };&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;const&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;decimal&lt;/span&gt;&lt;span style="color: #000000;"&gt; expected &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; 70m;&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Action&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 通过反射测试私有方法，比IDE提供的方式会更灵活一些&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var typeTarget &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt; (Order);&lt;br /&gt;            var methodTarget &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; typeTarget.GetMethod(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;CountTotalPrice&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, BindingFlags.Instance &lt;/span&gt;&lt;span style="color: #000000;"&gt;|&lt;/span&gt;&lt;span style="color: #000000;"&gt; BindingFlags.NonPublic);&lt;br /&gt;            var objResult &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; methodTarget.Invoke(target, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Assert&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            Assert.AreEqual(expected, (&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;decimal&lt;/span&gt;&lt;span style="color: #000000;"&gt;)objResult);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [DeploymentItem(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;1.1-ALite.Core.Tests\\1.xml&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;), TestMethod]&lt;br /&gt;        [DataSource(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Microsoft.VisualStudio.TestTools.DataSource.XML&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;|DataDirectory|\\1.xml&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Address&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, DataAccessMethod.Sequential)]&lt;br /&gt;        [Description(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;举例说明如何使用DataSourceAttribute属性, 该单元测试会读取数据源中的每一行数据进行测试&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;)]&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; TestCountTotalPriceWithVarious()&lt;br /&gt;        {&lt;br /&gt;            var province &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;._testContextInstance.DataRow[&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Province&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;br /&gt;            var city &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;._testContextInstance.DataRow[&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;City&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;br /&gt;            var fee &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; Convert.ToDecimal(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;._testContextInstance.DataRow[&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Fee&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]);&lt;br /&gt;&lt;br /&gt;            var address &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;.Format(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;{0} {1}&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, province, city);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var mockDeliveryManageHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Mock&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;IDeliveryManage&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;();&lt;br /&gt;            mockDeliveryManageHandler.Setup(e &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; e.CountDeliveryFee(It.IsAny&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;())).Returns(&lt;/span&gt;&lt;span style="color: #800080;"&gt;10.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Arrange&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var orderDetails1 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;            {&lt;br /&gt;                TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;10.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;            };&lt;br /&gt;&lt;br /&gt;            var orderDetails2 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;            {&lt;br /&gt;                TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;20.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;            };&lt;br /&gt;&lt;br /&gt;            var orderDetails3 &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; OrderDetail()&lt;br /&gt;            {&lt;br /&gt;                TotalPrice &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800080;"&gt;30.00m&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;            };&lt;br /&gt;&lt;br /&gt;            var details &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; List&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;OrderDetail&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; { orderDetails1, orderDetails2, orderDetails3 };&lt;br /&gt;&lt;br /&gt;            var target &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Order()&lt;br /&gt;            {&lt;br /&gt;                Destination &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; address,&lt;br /&gt;                DeliveryManageHandler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; mockDeliveryManageHandler.Object,&lt;br /&gt;                OrderDetails &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; details&lt;br /&gt;            };&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;decimal&lt;/span&gt;&lt;span style="color: #000000;"&gt; expected &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; fee;&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Action&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 通过反射测试私有方法，比IDE提供的方式会更灵活一些&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            var typeTarget &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt;(Order);&lt;br /&gt;            var methodTarget &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; typeTarget.GetMethod(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;CountTotalPrice&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, BindingFlags.Instance &lt;/span&gt;&lt;span style="color: #000000;"&gt;|&lt;/span&gt;&lt;span style="color: #000000;"&gt; BindingFlags.NonPublic);&lt;br /&gt;            var objResult &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; methodTarget.Invoke(target, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Assert&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            Assert.AreEqual(expected, (&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;decimal&lt;/span&gt;&lt;span style="color: #000000;"&gt;)objResult);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2102669.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/07/11/2102669.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2099407.html</id><title type="text">My Record Series: (1.3) Windows Phone 7 Application Events</title><summary type="text">Application_Launching 发生时机 用户从已安装应用列表点击并启动应用，一个应用实例被创建 动作 不要从独立存储中去读取配置信息，那样会降低加载速度； 不要尝试去恢复临时性状态数据（内存变量）； 当一个应用被启动，它会总是被当做一个新的实例展现。（不是接着上次操作）Application_Activated 发生时机 触发这个需要同时满足两个条件： 1. 用户使用Launcher，Chooser或是启动了另一个应用，导致离开了当前应用； 2. Launcher或Chooser操作完成后返回到应用或Back按键返回到应用。 这个事件不会在应用首次启动时被激活。 动作 应用应允许</summary><published>2011-07-06T09:26:00Z</published><updated>2011-07-06T09:26:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2099407.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2099407.html"/><content type="html">&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;strong&gt;Application_Launching&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 16px;"&gt;&lt;strong&gt;发生时机&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;用户从已安装应用列表点击并启动应用，一个应用实例被创建&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 16px;"&gt;&lt;strong&gt;动作&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;不要从独立存储中去读取配置信息，那样会降低加载速度；&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;不要尝试去恢复临时性状态数据（内存变量）；&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;当一个应用被启动，它会总是被当做一个新的实例展现。（不是接着上次操作）&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: 18pt;"&gt;Application_Activated&lt;span style="font-size: 16px;"&gt;&lt;br /&gt;发生时机&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span style="font-size: 13px;"&gt;触发这个&lt;/span&gt;&lt;span style="font-size: 18pt;"&gt;&lt;span style="font-size: 16px;"&gt;&lt;span style="font-size: 13px;"&gt;需要同时满足两个条件：&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;1. 用户使用Launcher，Chooser或是启动了另一个应用，导致离开了当前应用；&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2. Launcher或Chooser操作完成后返回到应用或Back按键返回到应用。&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;这个事件不会在应用首次启动时被激活。&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;动作&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;应用应允许用户像从未离开这个应用一样继续操作；&lt;/span&gt;&lt;br /&gt;临时性数据应该被恢复，但不应尝试从独立存储中读取以避免导致运行变慢的可能性。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;strong&gt;Application_Deactivated&lt;/strong&gt;&lt;span style="font-size: 16px;"&gt;&lt;br /&gt;&lt;strong&gt;发生时机&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;用户使用Launcher, Chooser或是启动了另一个应用，导致离开了当前应用&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;但这个事件不会在程序关闭时发生&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;动作&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;应该保存所有临时性状态（例如当前应用的会话信息）到State dictionary;保存所有持久化信息到独立存储。&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;该事件最大允许执行时间为10秒，如果超过10秒还未能执行完成，程序将被关闭而不是进入墓碑化。&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;strong&gt;Application_Closing&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 16px;"&gt;&lt;strong&gt;发生时机&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;使用Back按键退出应用程序的第一页&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 16px;"&gt;&lt;strong&gt;动作&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;保存所有持久化信息到独立存储&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;span style="font-size: 16px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2099407.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2099407.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2098799.html</id><title type="text">My Record Series: (1.2) Windows Phone 7 Launchers and Choosers</title><summary type="text">区别：Launcher不会返回调用程序任何信息，例如发送邮件任务；Chooser会返回一定的信息给调用程序，例如相册选择。Launchers启动器：EmailComposeTask：启动发送Email的应用程序。MarketplaceDetailTask：启动Marketplace客户端应用程序，并显示指定应用的详细信息。MarketplaceHubTask：启动Marketplace客户端应用程序。MarketplaceReviewTask：启动Marketplace客户端应用程序的审查页面。MarketplaceSearchTask：启动Marketplace客户端应用程序的搜索页面。Me</summary><published>2011-07-05T18:56:00Z</published><updated>2011-07-05T18:56:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2098799.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2098799.html"/><content type="html">&lt;p&gt;区别：Launcher不会返回调用程序任何信息，例如发送邮件任务；Chooser会返回一定的信息给调用程序，例如相册选择。&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;strong&gt;Launchers启动器：&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;EmailComposeTask&lt;/strong&gt;：启动发送Email的应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;MarketplaceDetailTask&lt;/strong&gt;：&lt;/strong&gt;启动Marketplace客户端应用程序，并显示指定应用的详细信息。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;MarketplaceHubTask&lt;/strong&gt;：启动Marketplace客户端应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;MarketplaceReviewTask&lt;/strong&gt;：启动Marketplace客户端应用程序的审查页面。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;MarketplaceSearchTask&lt;/strong&gt;：启动Marketplace客户端应用程序的搜索页面。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;MediaPlayerLauncher&lt;/strong&gt;：启动MeidaPlayer应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PhoneCallTask&lt;/strong&gt;：启动打电话应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;SearchTask&lt;/strong&gt;：启动搜索应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;SmsComposeTask&lt;/strong&gt;：启动发短信应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;WebBrowserTask&lt;/strong&gt;：启动IE。&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 18pt;"&gt;&lt;strong&gt;Choosers选择器：&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;EmailAddressChooserTask&lt;/strong&gt;：启动选Email地址界面，选择后返回Email地址。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CameraCaptureTask&lt;/strong&gt;：启动照相界面，拍照后返回照片图像。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PhoneNumberChooserTask&lt;/strong&gt;：启动电话号码选择界面，选择后返回电话号码。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PhotoChooserTask&lt;/strong&gt;：启动图片选择界面，选择后返回图像信息。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;SaveEmailAddressTask&lt;/strong&gt;：启动保存Email地址界面，选择后返回保存是否成功。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;SavePhoneNumberTask&lt;/strong&gt;：启动保存电话号码界面，选择后返回保存是否成功。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2098799.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/07/06/2098799.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/06/11/2078581.html</id><title type="text">My Record Series: (1.1) Windows Phone 7.1 Development Environment</title><summary type="text">Installation&amp; Sequence 1. Visual Studio 2010 Ultimate English Edtion 2. Visutal Studio 2010 Service Package 1 Full Download 3. Expression Studio 4.0 Dreamspark Edtion 4. Blend Service Package 1 5.Windows Phone Developer Tools 7.1 BetaNotification: 1. You can install all for them in a VM, but the</summary><published>2011-06-11T13:57:00Z</published><updated>2011-06-11T13:57:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/06/11/2078581.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/06/11/2078581.html"/><content type="html">&lt;p&gt;&lt;nobr&gt;Installation&lt;/nobr&gt;&amp;nbsp;&amp;amp; Sequence&lt;/p&gt;&lt;p&gt;1. &lt;a href="ed2k://|file|en_visual_studio_2010_ultimate_x86_dvd_509116.iso|2446950400|0694CFA0756D8C11499DE5B42E205410|/"&gt;Visual Studio 2010 Ultimate English Edtion&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2. &lt;a href="http://go.microsoft.com/fwlink/?LinkId=210710"&gt;Visutal Studio 2010 Service Package 1 Full Download&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3. Expression Studio 4.0 Dreamspark Edtion&lt;/p&gt;&lt;p&gt;4. &lt;a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=17872A3A-4620-4B87-9D62-F29173D12625"&gt;Blend Service Package 1&lt;/a&gt;&lt;/p&gt;&lt;p&gt;5.&amp;nbsp;&lt;a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=77586864-ab15-40e1-bc38-713a95a56a05&amp;amp;displaylang=en"&gt;Windows Phone Developer Tools 7.1 Beta&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Notification:&amp;nbsp;&lt;/p&gt;&lt;p&gt;1. You can install all for them in a VM, but the emulator of Windows Phone doesn't support to run in a VM.&amp;nbsp;&lt;/p&gt;&lt;p&gt;2. By default, Windows Phone Developer Tools DOESN'T support other Windows version except Windows 7 and Vista, even if Windows Server 2K8R2.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Modify the element&amp;nbsp;[gencomp7788] in the&amp;nbsp;baseline.dat on the WPDT ISO image: Update the value from 1 to 0.&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/2078581.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/06/11/2078581.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/03/02/1964051.html</id><title type="text">结合测试驱动TDD实施单元测试UnitTest</title><summary type="text">在之前的文章《单元测试培训系列：（一）单元测试概念以及必要性》中最后一段有提到，单元测试其实是完全为了测试先行，测试驱动准备的，并简单阐述了一下实施的流程，很多朋友对此很感兴趣，希望能更深入了解具体是如何实施的。 隔离，是单元测试中最重要的概念。一个被单元测试的方法，需要与所有依赖项进行隔离。而依赖项包括了环境的依赖项（I/O，网络，数据库，系统时间等）以及外部类和方法的依赖。因此，隔离性保障了单元测试是最小粒度的测试。 但隔离也导致了单元测试的局限性，主要是以下两个方面： 1. 通过单元测试是不能检测到一个方法修改后对系统的影响范围的。 单元测试因为隔离了对其他方法的依赖，因此当一个方法因.</summary><published>2011-03-01T17:08:00Z</published><updated>2011-03-01T17:08:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/03/02/1964051.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/03/02/1964051.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在之前的文章《&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html"&gt;单元测试培训系列：（一）单元测试概念以及必要性&lt;/a&gt;》中最后一段有提到，单元测试其实是完全为了测试先行，测试驱动准备的，并简单阐述了一下实施的流程，很多朋友对此很感兴趣，希望能更深入了解具体是如何实施的。 &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&lt;font size="4"&gt;隔离&lt;/font&gt;&lt;/strong&gt;，是单元测试中最重要的概念。一个被单元测试的方法，需要与所有依赖项进行隔离。而依赖项包括了环境的依赖项（I/O，网络，数据库，系统时间等）以及外部类和方法的依赖。因此，隔离性保障了单元测试是最小粒度的测试。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但隔离也导致了单元测试的&lt;strong&gt;&lt;font size="4"&gt;局限性&lt;/font&gt;&lt;/strong&gt;，主要是以下两个方面：&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. &lt;strong&gt;通过单元测试是不能检测到一个方法修改后对系统的影响范围的。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 单元测试因为隔离了对其他方法的依赖，因此当一个方法因为重构或者修改BUG等原因进行了改变时，运行已有的单元测试只能检测到这个被修改的方法本身是否依然符合以前预期的目标；而对修改这个方法对整个系统有任何影响，是完全无法通过运行单元测试得知的！！&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 很多朋友一直错把集成测试和单元测试混为一谈，认为单元测试能够检测到一个方法改变后对整个系统哪些部分造成了影响，这种想法很显然是错误的：因为单元测试的每个测试方法都把被测试的方法和其他方法、外部环境隔离开来，每一个被测试的方法都不依赖其他方法的具体实现，因此，即便其他类或方法的实现发生了改变，只要接口依然保持原样，对当前的单元测试是都不会产生任何影响的！！&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. &lt;strong&gt;单元测试对于需求变更基本没有太大作用。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 需求发生变更，首先要改变的就是单元测试！因为单元测试的关注点是每个方法的进出项（输入值和输出值）是否满足期望值。当发生需求变更时，意味着相关方法的预期值发生改变，此前相关的单元测试不再具有价值，需要重新编写。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为以上两个原因，对一个已有系统的代码追加单元测试的价值也变得非常鸡肋了。对于一个已有系统追加单元测试之后，单元测试唯一能在某个方法的内部实现进行重构的时候起到作用（例如修改BUG和算法优化，并且是在不修改当前调用关系以及相关接口的前提下）&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;基于测试先行来使用单元测试&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 说了这么多局限性，估计很打击大家积极性，难道单元测试就那么一无是处么？非也，而是单元测试的使用场景没对。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 测试先行还是测试驱动也好，都是目标导向方法论的具体实践，其目的都是在编写代码之前先行确定好要编写代码的目标以及校验方式。而再来看单元测试，单元测试只关注一个单独方法本身的功能以及这个方法的进出项是否满足期望值。这根本上就是和测试先行的目标是完全吻合的。可以说单元测试是测试先行的具体实施办法。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 有了这个基调，我们再来看如何把单元测试按照测试先行的指导来进行实施，因为该篇文章的关注点还是单元测试，就不过多探讨如何实施测试先行或者测试驱动。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们直接从拿到一个具体的业务功能模块开始。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;一、设计简要的类图以及类之间关系&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们首先简要的为该模块设计出基本的类图和类之间的关系（虽然有些敏捷方法对测试先行的要求是不做设计，只做测试用例，然后再编写测试代码和实现代码，但在这里个人还是按照先设计功能的类结构方式）&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在这个阶段，基本只需要定义好几个类，而类里面的成员则没有必要在一开始就全部设计出来。（推荐使用Visual Studio里的项目选项View Class Diagram进行代码和类设计的同步进行） &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 来看下面这个例子：一个订单系统，订单明细里面的每条订单的产品分为服装和数码产品类，而服装的价格来源是Vancl，数码产品的价格来源是Newegg. 首先这个订单系统需要一个订单统计总价格的功能。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Xrinehart/201103/20110301033732808.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Xrinehart/201103/201103010337338234.png" width="867" height="412" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如上图所示，定义了主要的类以及彼此之间的关系, 除了一些数据属性外，还没有定义这几个类的方法。 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductOrder类， 即定单类，需要实现方法Count，包含一个IList&amp;lt;BaseOrderDetail&amp;gt;类型的属性OrderDetails。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; BaseOrderDetail类，抽象类，包含ProductID和Amount属性以及一个IPriceProvide类型的属性PriceProvider，以及一个Count方法，即每条明细合计自己的总价。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ClothingOrderDetail类，BaseOrderDetail的子类，即服装类的订单明细，该类的PriceProvider属性应该为VanclProvider类的实例。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DigialOrderDetail类，BaseOrderDetail的子类，即数码类的订单明细，该类的PriceProvider属性应该为NeweggProvider类的实例。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IPriceProvide接口，该接口定义提供了从第三方获取产品价格的查询方法QueryPriceByProductID。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; VanclProvider类，实现了IPriceProvide接口，提供Vancl的产品价格查询。&lt;/p&gt; &lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NeweggProvider类，实现了IPriceProvide接口，提供Newegg的产品价格查询。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;二、为某一个类上的某一个公开方法编写单元测试&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当类以及类之间关系设计好之后，就开始根据业务功能的需要，逐步设计类的成员以及方法以实现这个类的功能。而在设计一个类的方法时，则是根据业务需求的要求来设计的，因此在编写一个类中的公开方法时，对这个类需要达成什么样的效果，应该是非常明确的。在明确了目标之后，其实编写单元测试就已经可以实现了，尽管现在实现代码还根本不存在。根据上面的例子，我们首先来编写ProductOrder类的Count方法的单元测试，此时，Count方法是没有真正实现的，甚至Count方法都还不存在（在单元测试代码中使用dynamitic关键字调用不存在的Count方法）。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductOrder类的Count方法，其实是统计所有单据中包含的所有货物的价格，因此可以分析得知Count方法依赖于BaseOrderDetail类的Count方法，并且属性OrderDetails会包含多个BaseOrderDetail类，这意味着在编写单元测试时，我们需要把这些BaseOrderDetail类都使用Mock对象代替。&lt;/p&gt;&lt;span style="display: none" id="Code_Open_Text_679953"&gt;  &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_154220" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_154220').style.display='none'; document.getElementById('Code_Open_Image_154220').style.display='inline'; document.getElementById('Code_Open_Text_154220').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_154220" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_154220').style.display='none'; getElementById('Code_Closed_Image_154220').style.display='inline'; getElementById('Code_Closed_Text_154220').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_154220" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_154220"&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// A test for the method Count&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;///&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        [TestMethod()]&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestCount()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;br/&gt;            dynamic target = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ProductOrder();&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; expected = 8748.55m;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Mock a jacket order detail and set return value of the method Count(), no matter real price and amount.&lt;/span&gt;&lt;br/&gt;            Mock&amp;lt;BaseOrderDetail&amp;gt; mockJacketOrderDetail = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Mock&amp;lt;BaseOrderDetail&amp;gt;();&lt;br/&gt;            mockJacketOrderDetail.Setup(e =&amp;gt; e.Count()).Returns(350.55m);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Mock a iPAD order detail and set return value of the method Count(), no matter real price and amount.&lt;/span&gt;&lt;br/&gt;            Mock&amp;lt;BaseOrderDetail&amp;gt; mockiPadOrderDetail = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Mock&amp;lt;BaseOrderDetail&amp;gt;();&lt;br/&gt;            mockiPadOrderDetail.Setup(e =&amp;gt; e.Count()).Returns(3499.00m);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Mock a iPAD order detail and set return value of the method Count(), no matter real price and amount.&lt;/span&gt;&lt;br/&gt;            Mock&amp;lt;BaseOrderDetail&amp;gt; mockNotebookDetail = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Mock&amp;lt;BaseOrderDetail&amp;gt;();&lt;br/&gt;            mockNotebookDetail.Setup(e =&amp;gt; e.Count()).Returns(4899.00m);&lt;br/&gt;&lt;br/&gt;            target.OrderDetails = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;BaseOrderDetail&amp;gt;();&lt;br/&gt;            target.OrderDetails.Add(mockJacketOrderDetail.Object);&lt;br/&gt;            target.OrderDetails.Add(mockiPadOrderDetail.Object);&lt;br/&gt;            target.OrderDetails.Add(mockNotebookDetail.Object);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Acction&lt;/span&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; actual = target.Count();&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;br/&gt;            Assert.AreEqual(actual, expected);&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在这个单元测试代码中，我们因为估计到实现Count方法需要依赖于OrderDetails属性，并合计里面所有BaseOrderDetail对象的价格，而BaseOrderDetail的价格是通过BaseOrderDetail.Count()方法获得的。我们只是测试ProductOrder的Count()方法，因此不需要再深入思考BaseOrderDetail.Count是如何实现的，只需要对BaseOrderDetail.Count()方法进行Mock即可。&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此在这段代码中，添加了三个Mock的BaseOrderDetail对象，并分别设置他们的Count()方法返回值，最后把它们都加入到ProductOrder的OrdeDetails属性中。到此，这个单元测试就写完了，但其实此时，我们的Count方法还根本没实现。所以运行单元测试会抛出异常。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二、编写方法的真正实现逻辑&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 那么，接下来，我们来实现这个方法的实际逻辑。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_391339" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_391339').style.display='none'; document.getElementById('Code_Open_Image_391339').style.display='inline'; document.getElementById('Code_Open_Text_391339').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_391339" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_391339').style.display='none'; getElementById('Code_Closed_Image_391339').style.display='inline'; getElementById('Code_Closed_Text_391339').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_391339" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_391339"&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// Count the total of the all orders.&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; Count()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;            result = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.OrderDetails.Sum(e =&amp;gt; e.Count());&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; result;&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 现在再运行之前的单元测试，会发现该单元测试已经顺利通过。大家是否看明白：单元测试只需要知道对被测试方法的期望值以及该方法可能依赖的项即可编写，无论这些方法是否已经真的实现。甚至可能的依赖项都是可以不用一开始编写的，而可以等到实现代码时发现需要调用其他方法时，再修改相应的单元测试代码进行隔离。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在这里再多写一个例子，方便大家加深认识。这次对BaseOrderDetail上的Count方法编写单元测试。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_137843" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_137843').style.display='none'; document.getElementById('Code_Open_Image_137843').style.display='inline'; document.getElementById('Code_Open_Text_137843').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_137843" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_137843').style.display='none'; getElementById('Code_Closed_Image_137843').style.display='inline'; getElementById('Code_Closed_Text_137843').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_137843" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_137843"&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;///A test for Count&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;///&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        [TestMethod()]&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestCount()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;br/&gt;            BaseOrderDetail target = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ClothingOrderDetail();&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; expected = 1000.00m;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Action&lt;/span&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; actual = target.Count();&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;br/&gt;            Assert.AreEqual(expected, actual);&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这段代码是最简单的单元测试框架，如果执行肯定会因为Count方法还未实现而抛出异常。接下来我们编写Count方法的实现，&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_885365" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_885365').style.display='none'; document.getElementById('Code_Open_Image_885365').style.display='inline'; document.getElementById('Code_Open_Text_885365').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_885365" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_885365').style.display='none'; getElementById('Code_Closed_Image_885365').style.display='inline'; getElementById('Code_Closed_Text_885365').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_885365" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_885365"&gt;    &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style="color: #808080"&gt;/// A abstract class of BaseOrderDetail.&lt;/span&gt;&lt;br/&gt;    &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;abstract&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; BaseOrderDetail&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// The identity of the current product.&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; Guid ProductID { &lt;span style="color: #0000ff"&gt;get&lt;/span&gt;; &lt;span style="color: #0000ff"&gt;set&lt;/span&gt;; }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// The amount of the current detail.&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; Amount { &lt;span style="color: #0000ff"&gt;get&lt;/span&gt;; &lt;span style="color: #0000ff"&gt;set&lt;/span&gt;; }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// A third-part price provider.&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; IPriceProvide PriceProvider { &lt;span style="color: #0000ff"&gt;get&lt;/span&gt;; &lt;span style="color: #0000ff"&gt;set&lt;/span&gt;; }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// Count the totoal of the current oder detail&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;virtual&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; Count()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; price = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PriceProvider.QueryPriceByProductID(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.ProductID);&lt;br/&gt;            result = price * &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Amount;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; result;&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以看到BaseOrderDetail中的Count方法依赖于自身的属性PriceProvider，而该属性本应该是根据订单的类型指定对应的VanclProvder类的实例。如果此时，我们再运行单元测试，必然会引发空引用异常，原因是此时的PriceProvider属性并没有赋值。（在实际运行的代码中可以使用例如Autofac或者Unity之类的IOC容器实现初始化）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此，我们需要在单元测试中添加Mock对象，来隔离对外部对象的依赖。修改后的单元测试代码如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_203883" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_203883').style.display='none'; document.getElementById('Code_Open_Image_203883').style.display='inline'; document.getElementById('Code_Open_Text_203883').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_203883" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_203883').style.display='none'; getElementById('Code_Closed_Image_203883').style.display='inline'; getElementById('Code_Closed_Text_203883').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_203883" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_203883"&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;///A test for Count&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;///&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        [TestMethod()]&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestCount()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Arrange&lt;/span&gt;&lt;br/&gt;            BaseOrderDetail target = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ClothingOrderDetail();&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; expected = 5555m;&lt;br/&gt;&lt;br/&gt;            target.Amount = 100;&lt;br/&gt;&lt;br/&gt;            Mock&amp;lt;IPriceProvide&amp;gt; mockPriceProvider = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Mock&amp;lt;IPriceProvide&amp;gt;();&lt;br/&gt;            mockPriceProvider.Setup(e =&amp;gt; e.QueryPriceByProductID(It.IsAny&amp;lt;Guid&amp;gt;())).Returns(55.55m);&lt;br/&gt;&lt;br/&gt;            target.PriceProvider = mockPriceProvider.Object;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Action&lt;/span&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; actual = target.Count();&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Assert&lt;/span&gt;&lt;br/&gt;            Assert.AreEqual(expected, actual);&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 至此，两个单元测试都能顺利的通过验证。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;三、当需求发生改变时，修改单元测试以及实现代码&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们假定我们的开发已经进入到一定阶段（已经有了两个类和方法的实现，并已经稳定），这时候需求发生了变更，我们来看下此时应该如何进行操作。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第一种情况，需求变更为，如果订单明细是ClothingOrderDetail类型时，Count方法需要在价格乘以数量之后增加额外的10元邮费。这种情况，其实只需要在ClothingOrderDetail类中重载BaseOrderDetail的Count方法即可，因此编写新的单元测试和方法实现即可。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 从这里可以注意到一点单元测试的特性：虽然我们修改了BaseOrderDetail的需求和实现，但ProductOrder中Count方法的单元测试并无法检测到这种变化，甚至BaseOrderDetail的Count方法修改后会引发异常，也无法通过单元测试得知BaseOrderDetail中Count方法产生的异常会到只ProductOrder中的Count方法也会发生异常。这是单元测试的局限性。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二种情况，需求变更为，如果订单的总额度超过5000，可以享受减免100的优惠。此时，可以先修改ProductOrder.Count方法的单元测试中的期望值为8648.55，再修改实际的代码实现如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_780700" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_780700').style.display='none'; document.getElementById('Code_Open_Image_780700').style.display='inline'; document.getElementById('Code_Open_Text_780700').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_780700" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_780700').style.display='none'; getElementById('Code_Closed_Image_780700').style.display='inline'; getElementById('Code_Closed_Text_780700').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_780700" class="cnblogs_code_Collapse"&gt;Unit Test for Count&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_780700"&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// Count the total of the all orders.&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #808080"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; Count()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;decimal&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;            result = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.OrderDetails.Sum(e =&amp;gt; e.Count());&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #008000"&gt;// Rreat Offer&lt;/span&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (result &amp;gt;= 5000)&lt;br/&gt;            {&lt;br/&gt;                result = result - 100;&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; result;&lt;br/&gt;        }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以看到其实这是在实现代码中添加了一个条件判断分支，我们也许需要为条件分支再单独写一个单元测试（略）。&lt;span style="display: none" id="Code_Open_Text_780700"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第三种情况，即需求发生重大变化，导致代码结构发生严重变化，那么基本之前的所有单元测试和实现代码都已经作废，甚至编译无法通过。&lt;/p&gt;&lt;span style="display: none" id="Code_Open_Text_780700"&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="display: none" id="Code_Open_Text_780700"&gt;四、对某个方法内的实现进行优化&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="display: none"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实单元测试在开发完成后能起到的作用，仅限于这种场景，对一个方法的实现进行优化，例如VanclProvider类的QueryPriceByProductID方法由以前从数据库查询，改为先从本地缓存中读取，若缓存不存在才从数据库中读取，因为期望值并为发生改变，只是修改了这个方法的实现方式，因此在修改之后依然可以通过单元测试进行验收是否修改正确，即新的修改是否依然符合早期的需求（&lt;strong&gt;期望值没发生改变，则单元测试不发生改变&lt;/strong&gt;）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;误区：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 很多朋友一直把单元测试和集成测试搞混，因此一直有个误区：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 认为只要对所有方法都编写了单元测试，在将来代码发生变化之后，可以通过运行单元测试快速的通过计算机校验得到所有受当前代码变化影响的用例以及影响范围。实际上，如果你仔细看到这里，你应该已经知道，这其实是不可能的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 单元测试的隔离性，导致了代码的影响不会传递：一个方法的实现导致了异常，并不会影响到其他调用过它的方法在原有的单元测试中顺利通过（Mock方法代替了实际方法的执行）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过以上这些例子，描述了如何结合测试先行的思想去做单元测试，核心的一点即是：&lt;strong&gt;目标先行&lt;/strong&gt;，每实现一个方法之前先考虑这个方法要达到的目标是什么，再编写这个目标的检测手段（单元测试），最终去实现这个方法，并通过之前设想和编写的检测手段去&lt;strong&gt;验证这个实现是否已经达到预期的目标&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 另一方面，这个过程也强烈的反应出了，单元测试作为测试的一种手段，其实&lt;strong&gt;在代码实现后，即维护阶段能起到的作用非常的小&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当需求发生变化时，需求的预期值必然发生变化，意味着单元测试需要被修改或重新编写。此时，原有的单元测试已经起不到事后检查的作用了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只有在需求未发生改变时，对现有代码进行不涉及结构改变的优化时，用于修改后的代码是否依然满足之前的需求预期。但实际应用中，这种场景少之又少。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/1964051.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/03/02/1964051.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/01/23/1941800.html</id><title type="text">架构评审表(Architecture &amp;amp; Design Review Check List)</title><summary type="text">性能和可扩展性(Performance and Scalability) 可靠性(Reliability) 可用性(Availability) 可管理性(Manageability) 1. 性能和可扩展性(Performance and Scalability) 部署与基础架构 Deployment and Infrastructure 合理的使用分布式架构，只在必要时才引入分布式 Use distributed architectures appropriately. Do not introduce distribution unnecessarily. 谨慎地选择合适的分布式通讯的机制.</summary><published>2011-01-23T11:33:00Z</published><updated>2011-01-23T11:33:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/23/1941800.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/23/1941800.html"/><content type="html">&lt;ul&gt;   &lt;li&gt;性能和可扩展性(Performance and Scalability) &lt;/li&gt;    &lt;li&gt;可靠性(Reliability) &lt;/li&gt;    &lt;li&gt;可用性(Availability) &lt;/li&gt;    &lt;li&gt;可管理性(Manageability) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;1. 性能和可扩展性(Performance and Scalability) &lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;部署与基础架构 Deployment and Infrastructure&lt;/strong&gt;&lt;/p&gt;      &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;合理的使用分布式架构，只在必要时才引入分布式           &lt;br /&gt;Use distributed architectures appropriately. Do not introduce distribution unnecessarily.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;谨慎地选择合适的分布式通讯的机制           &lt;br /&gt;Carefully select appropriate distributed communication mechanisms.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;把频繁交互的组件都尽可能的放在相同的边界内，或者尽可能的靠近（就近原则）           &lt;br /&gt;Locate components that interact frequently within the same boundary or as close to each other as possible.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;在设计中，把基础架构的限制考虑在内           &lt;br /&gt;&lt;font style="background-color: #ffc000; "&gt;&lt;span style="background-color: #ffffff; "&gt;Take infrastructure restrictions into account in your design.&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;考虑网络带宽限制           &lt;br /&gt;Consider network bandwidth restrictions.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;明确资源限制           &lt;br /&gt;&lt;font style="background-color: #ffc000; "&gt;&lt;span style="background-color: #ffffff; "&gt;Identify resource restrictions.&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;确保你的设计不会制约纵向扩展           &lt;br /&gt;Ensure your design does not prevent you from scaling up.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;&lt;font color="#4bacc6"&gt;&lt;span style="background-color: #ffffff; color: #000000; "&gt;使用支持横向扩展和逻辑分层的设计，避免无意中引入的依附关系，使能够支持负载均衡&lt;/span&gt;&lt;em&gt;                &lt;br /&gt;&lt;/em&gt;&lt;/font&gt;Ensure your design does not prevent you from scaling out and it uses logical layers, does not unwittingly introduce affinity, and supports load balancing.&lt;/p&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;耦合和内聚 Coupling and Cohesion&lt;/strong&gt;&lt;/p&gt;      &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;确保你的设计是松散耦合的           &lt;br /&gt;Ensure your design is loosely coupled.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;在你的设计中体现适度的内聚，将相关的实体如类和方法分组            &lt;br /&gt;Exhibit appropriate degrees of cohesion in your design and group together logically related entities, such as classes and methods.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;有限制的使用后期绑定（如反射等），只在必要时适当地使用后期绑定            &lt;br /&gt;Restrict use of late binding and only use late binding where it is necessary and appropriate.&lt;/p&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;通讯 Communication&lt;/strong&gt;&lt;/p&gt;      &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;&lt;font style="background-color: #ffc000;"&gt;&lt;span style="background-color: #ffffff; "&gt;避免接口过度会话（即避免太小粒度的通讯接口） &lt;/span&gt;              &lt;br /&gt;&lt;/font&gt;Interfaces do not enforce chatty communication.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;确保你的应用程序只在必要的地方进行远程调用；通过客户端验证、客户端缓存以及批量作业最小化影响           &lt;br /&gt;Ensure your application only makes remote calls where necessary. Impact is minimized by client-side validation, client-side caching, and batching of work.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;优化远程数据交换（例如数据压缩，序列化等）            &lt;br /&gt;Optimize remote data exchange.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;选择适当的通讯加密机制            &lt;br /&gt;Choose appropriate secure communication mechanisms.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;使用消息队列解耦你系统的各个组件            &lt;br /&gt;Use message queues to decouple component parts of your system.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;使用消息队列,&amp;#8220;射后不理&amp;#8221;方式(fire-and forget)以及异步方法调用等，降低Long-running调用的影响            &lt;br /&gt;Mitigate the impact of long-running calls by using message queues, "fire-and forget" approaches, and asynchronous method calls.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;在应用程序域更合适的情况下不要使用进程            &lt;br /&gt;Do not use processes where application domains are more appropriate.&lt;/p&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;并发 Concurrency&lt;/strong&gt;&lt;/p&gt;      &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;&lt;font style="background-color: #ffc000; "&gt;&lt;span style="background-color: #ffffff; "&gt;不要在你的程序里为每一次请求创建线程，使用CLR提供的和Win32提供的线程池代替&lt;/span&gt;&lt;/font&gt;            &lt;br /&gt;In your application do not create threads on a per-request basis, and use the common language runtime (CLR) thread pool and Win32 thread pool instead.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;只把那些必须线程安全处理的类型置为线程安全            &lt;br /&gt;Only types that need to be thread-safe are made thread-safe.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;谨慎考虑锁的粒度            &lt;br /&gt;Carefully consider lock granularity.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;确保你的程序获取资源和锁尽可能的迟，释放他们尽可能的早，从而减少冲突可能            &lt;br /&gt;Ensure your application acquires shared resources and locks late and releases them early to reduce contention.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;选择恰当的同步基元           &lt;br /&gt;&lt;font color="#4bacc6"&gt;&lt;span style="color: #000000; "&gt;Choose appropriate synchronization primitives.&lt;/span&gt;&lt;/font&gt;&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;选择一个恰当的事务隔离级别            &lt;br /&gt;Choose an appropriate transaction isolation level.&lt;/p&gt;       &lt;/li&gt;        &lt;li&gt;         &lt;p&gt;确保让异步执行I/O密集任务，但对于CPU密集任务则就无需异步执行            &lt;br /&gt;Ensure your application uses asynchronous execution for I/O bound tasks and not for CPU bound tasks.&lt;/p&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;资源管理 Resource Management &lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;确保你的设计支持并高效的利用各种&amp;#8220;池技术&amp;#8221;       &lt;br /&gt;Ensure your design supports and makes effective use of pooling. &lt;/li&gt;      &lt;li&gt;确保你的程序对资源的获取和释放遵循尽量晚获取，即早释放       &lt;br /&gt;Ensure your application acquires resources late and releases them early.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;缓存 Caching&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;&lt;font style="background-color: #ffc000; "&gt;&lt;span style="background-color: #ffffff; "&gt;对那些代价大的数据（如数据获取，复杂运算以及呈现）使用缓存 &lt;/span&gt;            &lt;br /&gt;&lt;/font&gt;Use caching for data that is expensive to retrieve, compute, and render.&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;缓存适当的数据，如静态页面，输出数据的某些特定项，存储过程的参数以及查询结果等         &lt;br /&gt;Cache appropriate data such as relatively static Web pages, specific items of output data, stored procedure parameters, and query results.&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;不要对那些不稳定的数据使用缓存         &lt;br /&gt;Do not use caching for data that is too volatile. &lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;选择一个合理的缓存位置         &lt;br /&gt;Select an appropriate cache location.&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;选择一个合理的缓存过期策略         &lt;br /&gt;Select an appropriate cache expiration policy.&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;状态管理 State Management&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;你的设计应该尽可能的使用无状态的组件，除非你已经认真考虑对于可扩展性的负面影响后才决定使用状态化组件         &lt;br /&gt;Your design favors stateless components. Or, you considered the negative impact on scalability if you decided to use stateful components.&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;如果使用.NET Remoting并且需要支持负载均衡，你需要使用Single Call的服务器端激活模式(SAO)         &lt;br /&gt;If you use Microsoft&amp;#174; .NET Framework remoting and need to support load balancing, you use single call server-activated objects (SAO).&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;如果你使用WebServices, 你也需要使用一种基于消息的无状态模式       &lt;br /&gt;If you use Web services, you also use a message-based stateless programming model.&lt;/li&gt;      &lt;li&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;"&gt;如果你使用Enterprise Services, 也需要使用无状态的组件使其能够放入对象池中           &lt;br /&gt;&lt;/font&gt;&lt;/em&gt;If you use Enterprise Services, also use stateless components to facilitate object pooling.&lt;/li&gt;      &lt;li&gt;那些需要被放到状态存储中的的对象必须可序列化       &lt;br /&gt;Objects that you want to store in state stores support serialization. &lt;/li&gt;      &lt;li&gt;考虑ViewState对性能的影响       &lt;br /&gt;Consider the performance impact of view state.&lt;/li&gt;      &lt;li&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;"&gt;根据Session的并发量以及每用户使用的Session数据量来选择一个合适的Session状态存储模式           &lt;br /&gt;&lt;/font&gt;&lt;/em&gt;Use statistics relating to the number of concurrent sessions and average session data per user to help choose an appropriate session state store.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;数据结构与算法 Data Structure and Algorithms&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;           &lt;li&gt;确保你的设计使用了合适的数据结构       &lt;br /&gt;Ensure your design uses appropriate data structures. &lt;/li&gt;      &lt;li&gt;只要在万不得已的情况下使用自定义集合       &lt;br /&gt;Use custom collections only where absolutely necessary.&lt;/li&gt;      &lt;li&gt;如果你需要实现自己的集合请实现IEnumerable接口       &lt;br /&gt;Extend the IEnumerable interface for your custom collections.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;数据访问 Data Access&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;           &lt;li&gt;在各层之间传递数据时使用最合适的数据格式，认真的考虑性能影响       &lt;br /&gt;Pass data across the layers by using the most appropriate data format. Carefully consider the performance implications. &lt;/li&gt;      &lt;li&gt;通过Paremters（参数集合，.NET调用SQL的API）的形式调用参数过程进行数据访问       &lt;br /&gt;Use stored procedures with the Parameters collection for data access.&lt;/li&gt;      &lt;li&gt;只处理和传递那些你真正需要的数据       &lt;br /&gt;Only process the data that is required.&lt;/li&gt;      &lt;li&gt;为那些大数据量的查询结果集提供合适的数据分页解决方案       &lt;br /&gt;Where appropriate, provide data paging solutions for large result sets.&lt;/li&gt;      &lt;li&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;&lt;div&gt;对那些涉及多个资源管理器的事务，在事务上下文需要跨越多个部件时，可以采用企业服务的声明式事务来处理&lt;/div&gt;           Use Enterprise Services declarative transactions for transactions that span multiple resource managers or where you need to flow transaction context across components.&lt;/font&gt;&lt;/em&gt;&lt;/li&gt;      &lt;li&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;如果你要控制二进制大对象(BLOBs)，使用恰当的chunking技术，不要重复的移动相同的BLOB           &lt;br /&gt;If you manipulate binary large objects (BLOBs), use appropriate chunking techniques, and do not repeatedly move the same BLOB.&lt;/font&gt;&lt;/em&gt;&lt;/li&gt;      &lt;li&gt;把重复的数据访问代码集中放入Helper类中       &lt;br /&gt;Consolidate repeated data access code into helper classes.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;异常处理 Exception Handling&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;           &lt;li&gt;不要使用异常去控制正常的程序逻辑流程       &lt;br /&gt;Do not use exceptions to control regular application flow.&lt;/li&gt;      &lt;li&gt;使用明确的异常处理边界（防止异常信息扩散）       &lt;br /&gt;Use well-defined exception handling boundaries. &lt;/li&gt;      &lt;li&gt;结构化的异常处理是更好的错误处理机制，不要使用错误代码来反馈       &lt;br /&gt;Structured exception handling is the preferred error handling mechanism. Do not rely on error codes.&lt;/li&gt;      &lt;li&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;&lt;em&gt;只为特定的原因，并在真的需要捕捉的时候去捕捉异常           &lt;br /&gt;Only catch exceptions for a specific reason and when it is required.&lt;/em&gt;&lt;/font&gt;&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;类设计的考虑 Class Design Considerations&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;           &lt;li&gt;类拥有它们操作的数据       &lt;br /&gt;&lt;em&gt;&lt;font color="#4bacc6"&gt;Classes own the data that they act upon.&lt;/font&gt;&lt;/em&gt;&lt;/li&gt;      &lt;li&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;"&gt;只在必要时使用显性接口. 当你有一些公有的功能穿插于多个类时，使用显性接口实现多态以及版本化           &lt;br /&gt;&lt;/font&gt;Do not use explicit interfaces unnecessarily. Use explicit interfaces for versioning and for polymorphism where you have common functionality across multiple classes.&lt;/em&gt;&lt;/li&gt;      &lt;li&gt;除非真的需要，否则不要类里包含虚方法       &lt;br /&gt;Classes do not contain virtual methods when they are not needed.&lt;/li&gt;      &lt;li&gt;使用重载方法比使用可变参数方法更好       &lt;br /&gt;&lt;em&gt;&lt;font color="#4bacc6"&gt;Prefer overloaded methods to methods that take variable parameters.&lt;/font&gt;&lt;/em&gt;&lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;2. 可靠性 (Reliability)&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;设计方面 Designing for Reliability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;参照Windows Server 2003的应用程序开发规范         &lt;br /&gt;Follow the Application Specification for Microsoft Windows Server 2003 &lt;a href="http://www.microsoft.com/windowsserver2003/partners/isvs/appspec.mspx"&gt;http://www.microsoft.com/windowsserver2003/partners/isvs/appspec.mspx&lt;/a&gt;&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;把可靠性需求列入开发规范         &lt;br /&gt;Put Reliability requirements into the specification.&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;&lt;em&gt;良好的基础架构的使用           &lt;br /&gt;&lt;/em&gt;Use of good architectural infrastructure&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;把管理信息也放入应用程序中             &lt;br /&gt;Built Management Information into the application&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用冗余提高可靠性         &lt;br /&gt;Use Redundancy for Reliability&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用内置的应用程序健康状态检查         &lt;br /&gt;&lt;em&gt;&lt;font color="#4bacc6"&gt;Use Built-In Application health checks&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用可靠的错误处理         &lt;br /&gt;Use Consistent Error Handling&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;测试方面 Testing for Reliability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;           &lt;li&gt;       &lt;p&gt;使用组件压力测试         &lt;br /&gt;Use Component Stress Testing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用集成压力测试         &lt;br /&gt;Use Integration Stress Testing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用实际环境测试         &lt;br /&gt;Use Real-World Testing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用随机破坏测试         &lt;br /&gt;Use Random Destruction Testing&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;3. 可用性 (Availability)&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;设计方面 Designing for Availability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;通过使用群集减少计划外的宕机时间         &lt;br /&gt;Reduce Unplanned Downtime with Clustering&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用网络负载均衡         &lt;br /&gt;Use Network Load Balancing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用RAID磁盘阵列进行数据存储         &lt;br /&gt;Use Raid for Data Stores&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;减少计划内的宕机时间         &lt;br /&gt;Reduce Planned Downtime&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;隔离危险应用             &lt;br /&gt;Isolate Mission Critical Applications&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用队列         &lt;br /&gt;Use Queuing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用分布式文件系统（DFS）         &lt;br /&gt;Use Distributed Files System (DFS)&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;测试方面 Testing for Availability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;变更控制过程测试         &lt;br /&gt;Test the Change Control Process&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;灾难性故障测试         &lt;br /&gt;Test Catastrophic Failure&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;测试故障转移技术         &lt;br /&gt;Test the Failover Technologies&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;测试监控技术         &lt;br /&gt;Test the Monitoring Technology&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;测试Help Desk过程         &lt;br /&gt;Test the Help Desk Procedures&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;资源冲突测试         &lt;br /&gt;Test for Resource Conflicts&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4. 可管理性 (Manageability)&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;p&gt;&lt;strong&gt;设计方面 Designing for Manageability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;在应用程序中使用企业库的Logging框架以提高可管理性         &lt;br /&gt;Use Enterprise Library Logging Framework in Application to support Manageability&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用WMI         &lt;br /&gt;Use Windows Management Instrumentation&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用SMS服务器         &lt;br /&gt;Use Systems Management Server&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用性能监视         &lt;br /&gt;Use Performance Monitor&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用系统准备工具 (Sysprep. Exe)         &lt;br /&gt;Use System Preparation Tool&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用SNMP网络监控协议         &lt;br /&gt;Uses SNMP&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用HTTP监控工具         &lt;br /&gt;Use HTTP Monitoring Tool&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;使用MS Web兼容性分析工具         &lt;br /&gt;Use MS Web Capacity Analysis Tool&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;     &lt;p&gt;&lt;strong&gt;测试方面 Testing for Manageability&lt;/strong&gt;&lt;/p&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;p&gt;测试WMI         &lt;br /&gt;Test WMI &lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;测试群集配置         &lt;br /&gt;Test Cluster Configuration&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;测试网络负载均衡         &lt;br /&gt;Test Network load Balancing&lt;/p&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;p&gt;&lt;em&gt;&lt;font style="background-color: #ffc000;" color="#4bacc6"&gt;测试应用程序同步             &lt;br /&gt;Test Application Synchronization&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;     &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/1941800.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/23/1941800.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/01/09/1930879.html</id><title type="text">单元测试培训系列：（三）可测试性（Testability）与重构Refactoring</title><summary type="text">在单元测试培训系列：（一）单元测试概念以及必要性中，我们已经说过单元测试的定义是什么，里面有提到一个很重要的概念：隔离！ 是的，没有隔离就没有可测试性，也就没有单元测试。 可测试性Testability 下面我们具体解释下什么叫做可测试性Testability: 让你的代码变的更加松耦合(Loosely coupled)，让类与类之间的关联性降低，降低到可以个别独立存在，如此一来便可在彼此互不影响之下完成个别的单元测试，而这些类又能组合成一个有用的应用程式。 因为单元测试要尽可能的隔离与当前方法逻辑没有关系的方法以及外部资源（I/O文件，配置文件，数据库，网络以及静态变量等），即要求每段代</summary><published>2011-01-09T09:23:00Z</published><updated>2011-01-09T09:23:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/09/1930879.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/09/1930879.html"/><content type="html">&lt;p&gt;在&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html"&gt;单元测试培训系列：（一）单元测试概念以及必要性&lt;/a&gt;中，我们已经说过单元测试的定义是什么，里面有提到一个很重要的概念：&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;隔离&lt;/strong&gt;&lt;/span&gt;！ 是的，没有隔离就没有可测试性，也就没有单元测试。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;可测试性Testability&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;下面我们具体解释下什么叫做可测试性Testability:&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;让你的代码变的更加松耦合(Loosely coupled)，让类与类之间的关联性降低，降低到可以个别独立存在，如此一来便可在彼此互不影响之下完成个别的单元测试，而这些类又能组合成一个有用的应用程式。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为单元测试要尽可能的隔离与当前方法逻辑没有关系的方法以及外部资源（I/O文件，配置文件，数据库，网络以及静态变量等），即要求每段代码在不依赖其他额外方法以及外部资源的情况下依然可以正确执行！&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这么说也许你无法理解也很难想象应该如何做到，那么我们下来举个例子说明。&lt;/p&gt; &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_739293" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_739293').style.display='none'; document.getElementById('Code_Open_Image_739293').style.display='inline'; document.getElementById('Code_Open_Text_739293').style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top" height="16" width="11"&gt;&lt;img style="display: none;" id="Code_Open_Image_739293" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_739293').style.display='none'; getElementById('Code_Closed_Image_739293').style.display='inline'; getElementById('Code_Closed_Text_739293').style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top" height="16" width="11"&gt;&lt;span id="Code_Closed_Text_739293" class="cnblogs_code_Collapse"&gt;Code Sample&lt;/span&gt;&lt;span style="display: none;" id="Code_Open_Text_739293"&gt;   &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt; Testability&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;double&lt;/span&gt; ComplexCompute(&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; x, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; y, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; z)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #0000ff;"&gt;double&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;            CalcServiceWrapper wraper = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; CalcServiceWrapper();&lt;br/&gt;            var a = wraper.Multiply(x, y);&lt;br/&gt;            result = wraper.Divide(a, z);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; result;&lt;br/&gt;        }&lt;br/&gt;    }&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 以上代码中，可以看到Testability类的ComplexCompute方法中依赖于CalcServiceWrapper类，并调用了该类的Multiply和Divide方法，最终完成了自身的功能。但这个代码就不具有很好的可测试性，因为ComplexCompute无法离开CalcServiceWrapper类而正确编译执行。那么如何重构这段代码使它可以解耦从而具有良好的可测试性呢？&lt;strong&gt;&lt;font color="#ff0000"&gt;依赖倒置(Dependency Inversion)&lt;/font&gt;&lt;/strong&gt;可以做到！&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;依赖倒置&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 依赖倒置是一种设计模式，具体的概念大家可以参见：&lt;a href="http://hi.baidu.com/xiechengfa/blog/item/acee7b4494621e056b63e5d6.html" target="_blank"&gt;向依赖关系宣战&amp;#8212;&amp;#8212;依赖倒置、控制反转和依赖注入辨析&lt;/a&gt;, 在这里就不深入的去阐述依赖注入是怎么回事了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实，如果你已经仔细看完那篇文章或者你已经知晓那些知识，那么其实所谓的解耦重构也就很简单了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 依赖注入，有三种实现方式：属性注入，构造函数注入，方法参数注入；在实际项目中，属性注入是最常见的方式，我们下面就来用这种方式来对之前的代码进行解耦重构：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;img id="Code_Closed_Image_908113" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_908113').style.display='none'; document.getElementById('Code_Open_Image_908113').style.display='inline'; document.getElementById('Code_Open_Text_908113').style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top" height="16" width="11"&gt;&lt;img style="display: none;" id="Code_Open_Image_908113" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_908113').style.display='none'; getElementById('Code_Closed_Image_908113').style.display='inline'; getElementById('Code_Closed_Text_908113').style.display='inline';" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top" height="16" width="11"&gt;&lt;span id="Code_Closed_Text_908113" class="cnblogs_code_Collapse"&gt;Code Sample&lt;/span&gt;&lt;span style="display: none;" id="Code_Open_Text_908113"&gt;    &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt; Testability&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; ICalcService CalcService{ &lt;span style="color: #0000ff;"&gt;get&lt;/span&gt;; &lt;span style="color: #0000ff;"&gt;set&lt;/span&gt;; }&lt;br/&gt;        &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;double&lt;/span&gt; ComplexCompute(&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; x, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; y, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; z)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #0000ff;"&gt;double&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;            var a = CalcService.Multiply(x, y);&lt;br/&gt;            result = CalcService.Divide(a, z);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; result;&lt;br/&gt;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如上代码中可以看到，把ComplexCompute方法中用到的CalcServiceWrapper类以及实例化的代码去掉，而增加一个类型为ICalcService（其实也可以是CalcServiceWrapper）的公开属性，在ComplexCompute方法中通过调用该属性来实现调用逻辑。这时候我们再来看，有没有发现，CalcService.Multiply方法和CalcService.Divide方法，无论CalcService是否被赋值，被赋予任何对象并不会影响当前方法的编译通过。换句话说：&lt;em&gt;&lt;strong&gt;ComplexCompute方法不再依赖于CalcServiceWrapper类型是否实现以及如何实现。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可能说到这里，你依然无法理解，代码被重构后解耦了，但这跟可测试性又有多大的关系呢？很遗憾的是目前因为有个很重要的概念还没能介绍，在这里只能简单的提及，那就是&lt;font color="#ff0000"&gt;&lt;strong&gt;Mock Object，&lt;/strong&gt;&lt;font color="#000000"&gt;将会在后面的章节详细介绍，这里先简单带过&lt;/font&gt;&lt;/font&gt;&lt;font color="#000000"&gt;。所谓Mock Object其实就是个假对象，但它只是模拟一个对象中某个/些方法，输入某些参数，返回一个预先设定的值，不用真的去执行代码。这样在做单元测试的时候，我们可以给Testability类实例的CalcService属性设置一个Mock Object, 然后在我们测试其中的ComplexCompute方法的时候，我们可以保证里面的用到的CalcService方法都按照我们期望并设定的方式执行（返回的值），从而实现：我们测试Testability类中的ComplexCompute方法的时候，不会再担心CalcServiceWraper类的实现是否存在错误，或者发生什么问题，因为在ComlexCompute中执行的是Mock Object，而不是真正的CalcServiceWraper对象。&lt;strong&gt;&lt;em&gt;我们的单元测试于是就只是测试ComplexCompute方法，不再涉及其他方法&lt;/em&gt;&lt;/strong&gt;！&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;非可测试性场景Untestable Scene &lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上一段提到的是依赖倒置是最正规的符合Testability的代码模式，但实际应用中，我们可能无法做到完全很好的执行，但有几种场景我们是必须要特别注意并避免的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;1. 静态变量&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 静态变量是非常不符合Testability要求的（但在处理遗留系统Legacy System时会有特别的用处，后文会提及）。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 首先来看静态变量，因为静态变量是全局性的，而且是保持有状态的。在&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html"&gt;单元测试培训系列：（一）单元测试概念以及必要性&lt;/a&gt;中，我们有说过，&lt;em&gt;单元测试是测试的最小单位，必须可信任的，可重复执行的&lt;/em&gt;。因此，若使用静态变量，就会导致破坏单元测试的可重复执行性，并且可能干扰到别的单元测试方法。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 例如：在方法A中改变了全局静态变量StaticVariableA的值，而在方法B中又要去尝试读取这个静态变量的值，那么在单元测试执行的过程中，若执行方法A和方法B的单元测试方法顺序不同，或执行多次很可能每次结果都会不同，这样明显会导致我们的单元测试结果不再可信。因此，我们应该尽量避免静态变量的使用。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;2. 静态方法&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 静态方法的可测试性不强的原因倒不是因为静态方法自身有什么太大的问题，而是在编写单元测试的过程中受制于工具框架的限制：目前的开源Mocking Framework中都不支持对静态方法进行模拟，若大量使用静态方法，会在编写单元测试的时候遇到无法解耦的问题。当然利用一些商业Mocking Framework，如TypeMock，Moles等可以规避这个问题。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 除了静态方法外，因为同样的开源Mocking Framework项目的限制，sealed类也是可测试性很差的代码。具体原因，我们放到后面的Mock Object章节一起介绍。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;font size="3"&gt;&lt;strong&gt;3. 直接依赖外部环境&lt;/strong&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一般来说，单元测试是要求代码尽可能不直接依赖于I/O文件接口，数据库，网络环境甚至系统环境（如时间等），但实际上一个项目总会有一部分代码无可避免的需要去访问这些外部资源，但我们可以尽量的控制这些代码集中在较小的范围内，并通过提取接口的方式使其他代码和这些直接访问外部资源的代码之间解耦。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此，泛滥的在代码中随意使用访问外部资源的代码是非常Untestable的设计，例如：在程序中到处都是访问数据库的代码，DataContext出现在每层代码中；到处充满依赖于ASP.NET或者WCF环境的代码（HttpContext, HttpRequest等）。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们没办法实现对边界代码的单元测试，只能通过集成测试来检验，例如对数据库访问层代码的测试；对网络访问层代码的测试等。但我们必须尽可能的防止这些边界代码的扩散，要尽可能的使他们被隔离起来。这个问题因为涉及面很广，暂不展开说明，在接下来的篇幅中会专门针对Entity Framework和ASP.NET MVC的Testability进行优化分析。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://msdn.microsoft.com/en-us/ff714955.aspx" target="_blank"&gt;Testability and Entity Framework 4.0&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://msdn.microsoft.com/en-us/magazine/dd942838.aspx" target="_blank"&gt;Unit Testing ASP.NET MVC&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/1930879.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/09/1930879.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html</id><title type="text">单元测试培训系列：（一）单元测试概念以及必要性</title><summary type="text">说起单元测试，多数同学应该都知道或听过，可能不少同学认为自己也写过，甚至觉得单元测试很简单有什么好培训的？其实这个事情还真没想象的那么简单！我基本可以比较负责任的说，你若没深入对单元测试做过研究，不知道Mock对象为何物的话，那么可能你以前写过的单元测试压根就不是单元测试。单元测试是什么？这个问题其实并不太容易一两句话说得特别清楚。先借用下百度百科的定义： 单元测试是在软件开发过程中要进行的最低级别的测试活动，在单元测试活动中，软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。 从以上这句定义我们可以看到，两个提取到到两个非常关键的字：最小粒度、隔离单元测试是测试的最小</summary><published>2011-01-02T15:05:00Z</published><updated>2011-01-02T15:05:00Z</updated><author><name>風語者·疾風</name><uri>http://www.cnblogs.com/Xrinehart/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html"/><content type="html">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 说起单元测试，多数同学应该都知道或听过，可能不少同学认为自己也写过，甚至觉得单元测试很简单有什么好培训的？其实这个事情还真没想象的那么简单！我基本可以比较负责任的说，你若没深入对单元测试做过研究，不知道Mock对象为何物的话，那么可能你以前写过的单元测试压根就不是单元测试。&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;strong&gt;单元测试是什么？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这个问题其实并不太容易一两句话说得特别清楚。先借用下百度百科的定义： &lt;br /&gt;&lt;/p&gt;&lt;div&gt;&lt;u&gt;&lt;strong&gt;单元测试是在软件开发过程中要进行的&lt;/strong&gt;&lt;strong style="color: red;"&gt;最低级别&lt;/strong&gt;&lt;strong&gt;的测试活动，在单元测试活动中，软件的独立单元将在与程序的其他部分&lt;/strong&gt;&lt;strong style="color: red;"&gt;相隔离的情况下&lt;/strong&gt;&lt;strong&gt;进行测试。&lt;/strong&gt;&lt;/u&gt; &lt;br /&gt;&lt;br /&gt;从以上这句定义我们可以看到，两个提取到到两个非常关键的字：&lt;strong&gt;&lt;span style="color: red;"&gt;最小粒度&lt;/span&gt;&lt;/strong&gt;、&lt;strong&gt;&lt;span style="color: red;"&gt;隔离&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&lt;ul&gt;&lt;li&gt;单元测试是测试的最小单位，必须可信任的，可重复执行的。&lt;/li&gt;&lt;li&gt;&lt;div&gt;如果测试的范围轻易的就会扩展到其他类或同类的其他方法，那就不再是最小单位，也就不是单元测试了！&lt;/div&gt;&lt;p&gt;例如： &lt;br /&gt;类A中的方法CallMethod中调用了类B中的方法DoMethod，如果在编写测试的时候不把B类中的DoMethod隔离出来，造成单元测试CallMethod方法时，实际实行了DoMethod方法，那么这个测试方法不能算作是单元测试。（如何隔离会在后文详解）&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;单元测试的目的是什么？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;有人曾给我一段非常简单的代码片段：一个方法，里面只是调用若干个其他方法，甚至也都没有任何返回值，然后问我这种代码写单元测试有任何价值？！完全是浪费体力！！&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div&gt;&lt;!--&lt;br/&gt;&lt;br/&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/&gt;http://www.CodeHighlighter.com/&lt;br/&gt;&lt;br/&gt;--&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;ClassA&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;CallMethod()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoSomethingForYou();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoSomethingForThem();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoSomethingForMe();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;其实提出这个疑问主要有两个原因导致：未能理解单元测试的目的是什么以及这段代码的可测试性并不高。（可测试性以及如何提高将在后面章节介绍）&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;单元测试的目的&lt;/u&gt;&lt;u&gt;是用来确保程式的逻辑&lt;/u&gt;&lt;/strong&gt;&lt;strong style="color: red;"&gt;&lt;u&gt;如你预期&lt;/u&gt;&lt;/strong&gt;&lt;strong&gt;&lt;u&gt;的方式执行，而并不是用来验证是否符合客户的需求的！通过单元测试来建立一道坚实的保障，防止代码在日后的修改中不会被破坏掉&lt;/u&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;是不是很失望？单元测试并不是用来验证代码是否符合需求的。&lt;br /&gt;事实上，单元测试是白盒测试的一种，而且需要开发人员来完成，最好是谁开发的代码就该谁来编写单元测试代码，因为代码的编写者最熟悉该段代码的目的，进而编写出验证该目的是否达到的单元测试代码。&lt;br /&gt;单元测试并不能用来代替其他测试手段，不过是实践过程中确实会很有效的帮助开发人员自查代码，进而发现一些潜在的BUG。但这只是一个额外的收获，若不是采用TDD那样的测试先行开发方式，那么&lt;strong&gt;单元测试的根本目的不是用于也无法检验当前代码是否存在BUG的&lt;/strong&gt;！&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div&gt;上面有说到单元测试最好是由开发人员自己来编写，用于验证该段代码是否符合开发者开发的预期要求。这里可能会有个疑问，既然开发者自己已经很清楚自己想要 结果是什么，直接运行一遍代码实际跑一次，通过断点调试不是就可以很方便的验证了嘛？再通过编写代码的形式，甚至比开发这个功能本身更多的代码，去验证这 个方法是否符合编写的目的，不是很傻很笨很累的办法么？&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;也许通过一个大家经常会碰到的实际场景能更好的说明：&lt;/p&gt;&lt;p&gt;&lt;em&gt;一个项目开始，项目经理把需求拆解为若干个模块分发给不同的开发人员去完成。这样每个人可能只熟悉自己的那部分代码。当项目某个阶段开发完成并上线后，可能部分开发人员会离开项目进入别的新项目，留下个别人员继续维护；或者项目下阶段开发新进一大批人员并不熟悉当前项目；当然最常见的是，在修改BUG阶段是无法完全做到谁产生的BUG就安排谁去修改。&lt;br /&gt;那么这时候就会出现一种常见的情况：因为对当前代码要满足的各种目的不熟悉，在修改一个模块或者BUG的时候把原有正确的功能也影响到了！更要命的是，谁也不知道这个BUG出现了，等待测试人员需要去重新发现一遍。&lt;br /&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;于是项目经理会发现，每次只要做了代码修改，无论是重构还是功能新增修改，还是修改了BUG，都无法知道当前代码的健壮性，以前编写的东西是否依然正确可用？&lt;/em&gt; &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;strong&gt;&lt;em&gt;然而如果这个项目在一开始就编写了单元测试的话，我们可以通过方便的自动化单元测试框架运行所有的单元测试，进而检查在此次修改前的所有被单元测试所覆盖的代码是否依然正常运行（符合以前编写的单元测试期望，如果验证通过，则认为原有代码未受到影响）&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;由上我们可以看出，单元测试虽然增加了相当大的开发工作量，但对于一个长期不断改进和维护的项目而言，单元测试反而是消减整体成本的一个有效手段，它能及时而准确的发现在代码修改之后，原来对代码要求的功能是否都依然正确满足。&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;但这里有个严重缺陷：单元测试无法检测到某个方法修改后是否对其他方法造成了影响，只能检测到被修改的方法本身的原有目的是否被影响（这个将在下面的与集成测试的区别中详解）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;也因此，个人觉得单元测试最适合的场景是基于TDD开发。若需求发生改变，修改了一个方法，而多数情况下也会去修改单元测试代码，因为预期也发生了改变，这个时候又不能检测到对其他代码的影响，这时单元测试意义确实不大。&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;单元测试与集成测试的区别是什么？&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;多数人其实一直不能很好的区分集成测试和单元测试，甚至不少人一直理解的单元测试只能算是集成测试，但其实两者的概念是完全不同的。&lt;br /&gt;单元测试测试的对象是每一个独立的方法，而且尽可能的隔离方法和其他方法以及其他外界依赖项；&lt;/p&gt;&lt;p&gt;基层测试的测试对象是被单元测试检测后的方法与方法之间的调用关系，以及调用执行过程是否符合预期。&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;针对项目的一部份或全部进行测试，可以跨越不同的类别与方法，并可直接存取的外部资源。例如: File I/O, 数据库操作, 网络连接, &amp;#8230;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;通常做集成测试都会需要先设置(Configure)测试所需的环境，测试完毕后通常要清除测试所产生的残留资料，以利下次测试或避免影响其他整合测试的结果。&lt;/p&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;ClassInitialize Attribute&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;ClassCleanup Attribute &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;TestInitialize Attribute &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;TestCleanup Attribute&lt;br /&gt;以上这些属性常用于集成测试，不能出现在单元测试中。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-weight: bold;"&gt;单元测试的三大基本要素（Trustworthiness/Maintainability/Readability)&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;信任你的测试代码结果&lt;br /&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;&lt;em&gt;你是否能信任你的测试结果？&lt;/em&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;当它通过，我们有信心说被测试代码一定工作。&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;当它失败，它一定证明被测试代码是错误的。&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;如果你不断的对测试结果失去信心，那么你也不会继续坚持撰写单元测试。有&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;许多人搞不清楚单元测试与集成测试的差别，以致于感觉自己写的单元测试过于薄弱而不相信测试的结果。&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;如果你因为某些原因导致测试失败，直接去改Code或直接去改Test Code都不是好事，你的首要目的是要能找出测试失败发生的主因，而非只是看错误这件事，这样你才能信任你的测试程式。 &lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div&gt;测试代码的可维护性&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;是否能够持续的维护你的测试程式？&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;如何有效的降低维护测试程式的成本？&lt;br /&gt;PS:透过一些Testable Design Pattern 可以有效提升可维护性。例如: Repository Pattern, Service Pattern&amp;#8230;&amp;#8230;&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div&gt;测试代码的可读性&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;div&gt;&lt;em&gt;你的测试程式的命名是否易于理解?&lt;/em&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;当你测试失败时是否能从测试失败的测试方法(TestMethod)明确看出实际失败的原因？&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;当读取测试数据的人看不懂你的测试，人们就不会执行这些测试、也不会去维护这些测试，久而久之就会越来越恶化。&lt;/em&gt; &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;Test Driven Development &amp;amp; Unit Test&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  写在本文最后，其实我一直觉得单元测试其实是为了TDD开发模式而诞生的，在这种开发模式下使用单元测试完全是非常顺畅的：&lt;/div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  1、根据软件需求文档拆解软件功能，并设计出功能模块划分； &lt;br /&gt;&lt;p&gt;&lt;div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  2、根据需要的功能模块设计出单元测试场景用例，因为此时可以很清晰的知道能够提供什么样的数据，以及需要达到什么样的功能，这对设计单元测试用例已经完 全足够了；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  3、编写单元测试代码，这个时候可以专注于检验这个方法的是否满足设计的要求，此时甚至实际的代码还根本没开发，而.NET  4.0的Dynamic关键字在这里可以得到充分的发挥：调用那些根本都还不存在的方法，却不会导致编译无法通过。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  4、若在编写单元测试过程中，可以预期当前这个方法若需要调用一些其他类或方法的支持，可以通过编写Mock  Object来模拟，同样也是无需实现真正的代码，只需要有基本的代码框架或者接口即可。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;    5、在为这个方法编写好单元测试代码之后，就可以开始编写实际的代码实现了，因为在之前为了满足Testability的需要，代码已经是基于依赖倒置模  式的了，无需再担心其他需要调用的类或方法是否已经实现或正确实现。在编写好本方法的实现之后就可以通过运行之前的单元测试进行验收了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   可以看到，若按照以上这种方式进行开发，首先代码的耦合性是非常低的，其次代码的质量也是很高的，最后还会因为代码之间的耦合度低从而降低在开发过程中， 相互制约进度相互影响的可能性。在追查BUG的时候也很有优势：很容易查到BUG是否蔓延。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 反之，对一个Legacy  System进行重构使之Testable，再编写单元测试其实工作量不小，实际的收益也不会特别大。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;单元测试的基本概念以及价值就基本讲完，下篇文章将开始介绍Visual Studio 2010中的单元测试工具与环境。&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://www.cnblogs.com/Xrinehart/aggbug/1921279.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Xrinehart/archive/2011/01/02/1921279.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
