<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_关河的测试生活</title><subtitle type="text">生活不是测试，但测试确实是一种生活</subtitle><id>http://feed.cnblogs.com/blog/u/19737/rss</id><updated>2012-04-17T16:31:07Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/19737/rss"/><entry><id>http://www.cnblogs.com/guanhe/archive/2012/04/15/what_kind_of_testing_we_need.html</id><title type="text">我们需要什么样的测试？</title><summary type="text">左耳朵耗子发表了《我们需要全职的QA吗？》后，一石激起千重浪，赞成者有之，激烈反对者有之；有人说文中对QA的定义不对，还有人说以偏概全…… 的确，在需不需要专职的QA角色这个问题上，很难用一个简单的“需要”或“不需要”来回答。前两天我写了一篇对该文的回应文章，但由于文章写就得比较仓促，很多观点来不及完整表述，因此，在“真理越辩越明”的原则下，在这边文章中，我准备就“我们需要什么样的测试”这个问题说说我自己的看法。首先要说明的是，这篇文章完全不是讨论“我们是否需要专职QA”这个问题的，也不是讨论“各种情况下QA或测试工程需要做什么”，而是从我自身对测试的认知和个人经验出发，说一说我对不同特点的产品需要的测试的看法。</summary><published>2012-04-15T07:10:00Z</published><updated>2012-04-15T07:10:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2012/04/15/what_kind_of_testing_we_need.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2012/04/15/what_kind_of_testing_we_need.html"/><content type="html">&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;左耳朵耗子&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;发&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;表了&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;《&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;我&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;们&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;需要全&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;职&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;QA&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;吗&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;？》&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;后&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;一石激起千重浪&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;赞&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;成者有之&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;激烈反&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;对&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;者有之&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;；&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;有人&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;说&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;文中&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;对QA&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的定&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;义&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;不&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;对&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;还&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;有人&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;说&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;以偏概全&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8230;&amp;#8230; &lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的确&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;在需不需要&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;专职&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;QA&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;角色&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;这&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;个&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;问题&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;上&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;很&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;难&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;用一个&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;简单&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8220;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;需要&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8221;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;或&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8220;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;不需要&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8221;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;来回答&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;。&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;前两天我写了一篇&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;对该&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;文的回&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;应&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;文章&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;但由于文章写就得比&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;较仓&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;促&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;很多&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;观&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;点来不及完整表述&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;因此&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;在&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8220;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;真理越&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;辩&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;越明&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8221;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的原&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;则&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;下&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;在&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;这篇&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;文章中&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;，&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;我准&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;备&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;就&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8220;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;我&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;们&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;需要什么&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;样&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;的&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;测试&amp;#8221;这&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;个&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;问题说说&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;我自己的看法&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;首先要说明的是，&lt;strong&gt;这篇文章完全不是讨论&amp;#8220;我们是否需要专职QA&amp;#8221;这个问题的&lt;/strong&gt;，&lt;strong&gt;也不是讨论&amp;#8220;各种情况下QA或测试工程需要做什么&amp;#8221;&lt;/strong&gt;，而是从我自身对测试的认知和个人经验出发，说一说我对不同特点的产品需要的测试的看法。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;本文讨论的前提是：&amp;#8220;&lt;strong&gt;不同的产品需要不同的测试&lt;/strong&gt;&amp;#8221;。当我提到&amp;#8220;产品&amp;#8221;时，除了产品本身所对外展现的特性外，还会隐含地包含了该产品开发团队的状况。这篇文章没有把行业作为一个划分的维度，是因为我相信，即使在同一个行业中，也存在各种截然不同的产品。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;测试是为质量服务的，测试活动围绕质量进行。这个定义是我们今天讨论的出发点。ISO 9126模型给出了一个多层次的质量模型定义，该定义包括了各个正交维度的质量属性，这些质量属性中既有面向用户的，也有面向开发的。但在实际的测试工作中，一旦提到产品质量，大部分人更容易将其理解成&amp;#8220;用户质量&amp;#8221;，也就是&amp;#8220;最终用户所能感受到的软件的质量（例如，软件的功能性、性能、安全性等等）&amp;#8221;。&amp;#8220;用户质量&amp;#8221;是用户所能够直接感受到的产品的&amp;#8220;好坏&amp;#8221;，也是用户是否愿意为产品付钱的主要原因。因此，在测试中重视&amp;#8220;用户质量&amp;#8221;是必然的。设想一下，如果A公司要为B客户开发一个软件，只要该软件最终能够达到B用户的要求，A公司就能拿到钱，通常这也就意味着A公司&amp;#8220;成功的完成了该软件的开发&amp;#8221;。从这个角度来说，&amp;#8220;用户质量&amp;#8221;就是软件开发是否成功的标准。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;然而，如果深入看待整个软件开发过程，事情就没有这么简单了。A公司为客户B开发的软件并非是一锤子买卖，而是需要不断的维护和升级的，B用户不断提出新的需求，而这些新的需求都要被加入到软件中去。在这种情况下，从效益出发，A公司就不能仅仅考虑最终的产出是否能够满足B客户的要求了，而是必须想办法保证产品在持续演进的过程中始终保持好的可维护性和可测试性，这样A公司才能以较低的成本让这个产品持续成功。因此，如果不把软件开发看成一锤子买卖，而将该软件的生命周期的维护过程考虑进去，我们就不得不关注&amp;#8220;用户质量&amp;#8221;之外的&amp;#8220;开发质量&amp;#8221;，这里的&amp;#8220;开发质量&amp;#8221;就是指产品内在的，是否容易被修改、是否容易被移植、是否容易被验证的特点。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:22.7pt;line-height:15.75pt;"&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;1.&amp;#8220;&lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;用户质量&amp;#8221;和&amp;#8220;开发质量&amp;#8221;就是我通常用来分析一个产品究竟需要什么样的测试的第一个因素。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;在这个维度下，我们可以很容易地理解，如果一个产品仅仅是 &amp;#8220;一次性&amp;#8221;的产品（也即，开发后不再需要维护和持续演化），那么测试的重点一定就是&amp;#8220;用户质量&amp;#8221;（只需要关心该产品是否在用户面前表现得够好就可以了）；代码是不是够烂，设计是不是不合理并不重要。而如果一个产品是需要持续演进较长时间的，那就必须关心代码和设计的质量（&amp;#8220;开发质量&amp;#8221;）。例如，一个采用付费下载方式进行销售的小游戏，开发团队通常不会花太多的时间和精力在保证产品具有良好的&amp;#8220;开发质量&amp;#8221;上，而宁愿花更多的时间去调整外部的细节表现，音效，图像上。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:22.7pt;line-height:15.75pt;"&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;2. &lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;另一个直接决定了产品需要什么样的测试的纬度是&amp;#8220;产品对缺陷的容忍程度&amp;#8221;。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;测试工程师有时候喜欢把&amp;#8220;零缺陷&amp;#8221;作为标榜测试工作的口号（我在很长一段时间内也是如此），但，仔细想想，如果发现一个缺陷的成本比让这个缺陷留在产品中带来的损失更大，那是否还值得去发现这个缺陷？我想，从项目的角度来说，答案是不言而喻的。测试是一个资源权衡的活动，也是一个基于风险的活动，因此，产品的缺陷带来的影响越小，影响越容易被消除（修复），这个缺陷的价值就越小，值得投入用来发现缺陷的资源也就越少。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;拿互联网产品和传统的桌面产品来比较，对桌面型产品来说，缺陷的修复成本足够高（只能通过软件召回或是发布补丁的方式），因此对缺陷的容忍度就低；而对于互联网产品来说，由于其产品的修复成本足够低（对一个已知的缺陷，可能只需要花上几分钟到几十分钟就能完全修复了），因此相对而言，其对缺陷的容忍程度更高（当然，对于那些会导致用户数据损失或是带来其他不可逆破坏的缺陷，那又另当别论了），对互联网产品开发来说，及时识别缺陷（发现那些用户已经遇到的缺陷），快速定位缺陷和快速修复缺陷的能力往往要比在系统测试阶段发现缺陷的能力更重要。而要能有强大的识别缺陷和定位缺陷的能力，就必须依靠产品内建的&amp;#8220;开发质量&amp;#8221;了。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;再以医疗行业的某些软件为例，对涉及到医疗器械的控制软件（如自动注射器，心脏起搏器等），其对缺陷的容忍程度就非常低，原因很简单，因为这类缺陷可能带来的后果太严重。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:22.7pt;line-height:15.75pt;"&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;3. &lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;第三个因素是&amp;#8220;有效的测试方式&amp;#8221;。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;对互联网产品来说，最有效的测试方式也许并不是找一堆专职的测试工程师在公司内部尽可能地覆盖每一个功能细节，让真正的用户来对产品进行测试在很多情况下也许是更好的选择。无论是FB，Google等公司提倡的dog food（让全部员工来进行测试），还是在实际产品上进行的a/b testing, 或是游戏公司通常喜欢采用的内测方式，都是典型的让用户参与测试的方法。另外，根据产品不同的特性，适合采用手工测试还是自动化测试的方式来进行测试也是一个值得考虑的点。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:22.7pt;line-height:15.75pt;"&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;4. &lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;第四个因素则是&amp;#8220;产品的开发团队所处的状况&amp;#8221;，因此，对同一个组织来说，在组织发展的不同阶段，所需要的测试也是不同的。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;&amp;#8220;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;苦逼的团队是做不出有爱的产品的&amp;#8221;，自然，&amp;#8220;苦逼&amp;#8221;的团队也不可能达成好的测试。因为让每个人疲于奔命的是总也无法完成的任务和无止境的加班，恐怕都没有停下来思考如何改进的机会。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;以上就是我对&amp;#8220;什么样的产品需要什么样的测试&amp;#8221;这个问题的理解。在这四个因素的指导下，再回头来考虑不同软件产品的测试，就很容易理解为何不同的产品，不同的企业会采用很不相同的测试方式。例如，FB没有专职的测试工程师，因为通常意义上关注&amp;#8220;用户质量&amp;#8221;的测试工程师并不能在这个组织中发挥大的价值，只有对开发有深入了解的开发工程师才能真正的在提高&amp;#8220;开发质量&amp;#8221;方面发挥作用。而对于许多国内的以&amp;#8220;做项目&amp;#8221;为主的软件企业来说，也就很好理解为什么他们只需要&amp;#8220;能像客户一样发现产品中的缺陷的&amp;#8221;的测试了。&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&amp;nbsp;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:22.7pt;line-height:15.75pt;"&gt;&lt;strong&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;参考：&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;ISO 9126&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: 宋体; "&gt;质量模型：&lt;a href="http://en.wikipedia.org/wiki/ISO/IEC_9126"&gt;http://en.wikipedia.org/wiki/ISO/IEC_9126&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p align="left" style="margin-bottom:12.0pt;text-align:left; text-indent:21.0pt;line-height:15.75pt;"&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/guanhe/aggbug/2450210.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/guanhe/archive/2012/04/15/what_kind_of_testing_we_need.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html</id><title type="text">对《我们需要专职QA吗？》的回应</title><summary type="text">说实话，在我看来，左耳朵耗子的《我们需要专职的QA吗？》这篇文章的观点并不算过激。最多就是一篇从开发工程师的角度来商讨是否需要设立“专门做测试的岗位”，让“不熟悉或是不懂开发的人”来做测试工作。如果这个问题摆在我的面前，在大多数情况下，我的答案可能和左耳朵耗子一样：“不需要”。作为一个在测试行业工作了10多年的“老人”，在这里赞同左耳朵耗子的观点似乎是对自己过去这么多年工作的否定，但实际上，正是因为有这么多年的经验，我才真正能够深刻的体会专职测试工程师在工作中的局限和不足。</summary><published>2012-04-12T15:35:00Z</published><updated>2012-04-12T15:35:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html"/><content type="html">&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;p&gt;说实话，在我看来，左耳朵耗子的&lt;a href="http://coolshell.cn/articles/6994.html"&gt;《我们需要专职的QA吗？》&lt;/a&gt;这篇文章的观点并不算过激。最多就是一篇从开发工程师的角度来商讨是否需要设立&amp;#8220;专门做测试的岗位&amp;#8221;，让&amp;#8220;不熟悉或是不懂开发的人&amp;#8221;来做测试工作。如果这个问题摆在我的面前，在大多数情况下，我的答案可能和左耳朵耗子一样：&amp;#8220;不需要&amp;#8221;。&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;作为一个在测试行业工作了10多年的&amp;#8220;老人&amp;#8221;，在这里赞同左耳朵耗子的观点似乎是对自己过去这么多年工作的否定，但实际上，正是因为有这么多年的经验，我才真正能够深刻的体会专职测试工程师在工作中的局限和不足。&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;为了避免本文引发类似&amp;#8220;混淆了QA和测试角色&amp;#8221;之类的毫无营养的评价，在下文中，我一概使用&amp;#8220;测试人员&amp;#8221;来指代从事&amp;#8220;专职测试&amp;#8221;工作的角色，那些喜欢拿&amp;#8220;QA层次更高，是管过程的&amp;#8221;各位看管请自行绕道（顺便说一句，我在google的时候，是十分反感自己的团队被称作QA团队的，如果有人这样说，我一定会认真的纠正：&amp;#8220;不，我们不是QA，我们是测试工程师&amp;#8221;，关于google有没有QA，各位可以自行google）。&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&amp;#8220;软件需要测试&amp;#8221;应该是不会有人反对的观点。问题是，设置专职的&amp;#8220;测试人员&amp;#8221;是否会比&amp;#8220;让开发做测试&amp;#8221;更能有效的做好测试。从1998年开始，在华为我开始了自己的测试生涯。关于为什么需要测试工程师，在我的测试工程师职业生涯中，听到得最多的是两种论调：其中之一是&amp;#8220;测试工程师需要的技能与开发工程师不同，测试工程师需要的是发现问题的能力&amp;#8221;，另一种是&amp;#8220;开发人员无法保证产品质量，因此需要测试人员&amp;#8221;。后一种论调其实是有很大的问题的，&amp;#8220;开发人员无法保证质量&amp;#8221;不意味着测试人员就可以保证质量，在大多数企业中，说的不客气一点，&amp;#8220;保证质量&amp;#8221;通常只是测试部门可以继续存在的表面上的理由而已。至于说到开发工程师与测试工程师所需技能的不同，这一点倒是存在的事实。在一个组织中，测试人员通常会花主要的精力去设计测试用例，评价覆盖度，尝试从不同的角度攻击应用，从表面上看，的确，测试和开发需要的技能很不同。&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;但是，我要问两个问题：&lt;/div&gt;&lt;p&gt;&lt;ol&gt;&lt;li&gt;这些技能开发工程师不能具备吗？&lt;/li&gt;&lt;li&gt;设计测试用例，评价覆盖率这类工作是否真的需要专职的人员去做？&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;p&gt;所谓的黑盒测试技术，有多大的难度？平心而论，一个智商正常的具有较好计算机基础的人，一个下午就能完全理解常用的黑盒测试技术，白盒测试技术也不会难到哪里去。只要开发工程师愿意，这些工作他们完全可以承担。只所以开发工程师没有承担这些任务，原因恐怕不是他们不能做，而是像在《我们需要专职的QA吗？》文章后的评论中某位做开发的仁兄说的那样：&amp;#8220;如果有一个比较专业的QA来帮助我们，我们就能把自己的时间花在更有用的地方&amp;#8221;。&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;社会分工的细化自然是提供效率的方式，但社会的发展并不只伴随着分工的细化，由于开发工具和开发基础的变化，分工的&amp;#8220;合并&amp;#8221;也是一个一直在持续的趋势。几年前，大多数公司都倾向于有单独分工的&amp;#8220;前端工程师&amp;#8221;和&amp;#8220;后端工程师&amp;#8221;，但现在的趋势不也是在融合？至少，Facebook就要求自己的工程师能同时承担前后端的任务，google也是如此。测试工作和开发工作难道就不能融合？让开发人员做测试怎么就不行？&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;其实，《我们需要专职的QA吗？》中的不少观点我都非常赞同，鉴于左耳朵耗子已经写了这么大一篇，我就不再重复这些观点了，作为对这些观点的一些佐证，我来说说我自己经历过的几件事情。&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;fieldset&gt;&lt;legend&gt;故事1&lt;/legend&gt;在Google中国的时候，我们团队负责的某个项目，开发工程师每天忙死忙活的加新功能，赶上线，整个团队的开发和SET（Software Engineer in Test）都忙得不行。从传统的对测试工程师的角度来评价的话，这个团队唯一的一个SET工作得十分出色：她几乎能发现所有的缺陷，她几乎把自己所有的时间都投入到项目中去发现缺陷；她是整个开发团队最喜欢和最感激的人，因为&amp;#8220;没有她，这个产品简直不可能发布&amp;#8221;。但是，这个产品在发布了一段时间后，每个RC的缺陷始终居高不下，这位尽职的SET几乎投入了全部时间和精力，仍然无法让这个产品的质量提高分毫。为什么？因为开发人员从来就没有意识到他们的代码有多烂！当有一个可以帮你发现所有错误的人的时候，我相信，你犯错的勇气一定会更大。这个问题最后是如何解决的？说起来很讽刺，解决这个问题的第一步就是让开发意识到&amp;#8220;你们需要自己为代码质量承担责任&amp;#8221;，当这位SET改变工作方式，不再尝试把自己的业务时间全部投入来发现无尽的缺陷之后，开发人员立刻意识到自己遇到了大麻烦。然后，他们主动来找我商讨解决方案，当他们最终发现自己不得不改变自己的做法，自己来控制自己错误的时候，事情立刻开始好转。当他们的单元测试达到40%的覆盖率的时候，所有人都变得更轻松了。这位SET负责推动了单元测试，推动了为了让代码具有良好可测试性而进行的重构，设立了组织的代码提交规则（强制提交的新代码必须包含单元测试），然后，产品质量在接下来的一段时间内持续上升。&lt;/fieldset&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;fieldset&gt;&lt;legend&gt;故事2&lt;/legend&gt;Google内部有一个Test Certified的认证，该认证是针对开发团队进行的。认证的主要要点都是基于单元测试覆盖率，基于持续集成建立的开发规则，自动化测试（以小测试为主）。这个认证分为5个级别，1级最低，5级最高。Test Certified和CMMI一样有5个级别，可是出发点却大不相同。这个认证中涉及的全部事情都能（且主要是）由开发工程师搞定，越接近高的级别，需要的专职SET越少。（详细内容参见James最近出版的《How&amp;nbsp;google test software》，虽然书中的观点有些和我不一致，但在Test Certified的描述上是完全没有问题的）&lt;/fieldset&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;fieldset&gt;&lt;legend&gt;故事3&lt;/legend&gt;最后一个故事是最近我遇到的一个测试工程师的故事。她从一开始就表明自己有很强的&amp;#8220;做自动化测试&amp;#8221;的意愿，因此，她所在的项目团队的技术负责人很高兴地给她分配了一些技术性的任务，包括使用工具监控应用的内存使用情况，找到一个方案能够方便的定位crash发生等等，谁知道这位测试工程师的第一反应是，&amp;#8220;这难道不是开发工程师的活吗？&amp;#8221;，在她看来，测试工程师完全不应该了解程序是如何工作的，所谓的自动化测试应该是&amp;#8220;使用某种手段把自己现在的手工劳动重复下去&amp;#8221;。&lt;/fieldset&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;专职测试人员是否毫无存在的必要？当然不是。至少，我们必须承认，在有些必须大量依靠&amp;#8220;体验&amp;#8221;进行测试的行业，如游戏行业中，专职的测试人员是有存在的必要的。但我想，在类似google，facebook这样的环境中（我猜测在左耳朵耗子所在的环境中也差不多），不能深刻理解开发和具有深入的开发技术的测试人员（SET）的确没太多价值。真诚的希望各位测试工程师在读左耳朵耗子的文章时，不要纠结于他的结论，而去看看他提到的问题，是不是真的切中了专职测试的痛处。至少对我来说，文章中提到的这些熟悉的问题每一个都能让我想起一些故事。&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;对于我从事了10多年的测试行业，即使我现在的角色有所变化，这个行业的每一个变动和变革都会让我关注。这种感情是不可能割舍的。所以，真诚的希望每一位测试的工作者，能够真正思考我们如何做的更好。测试和开发之间有更多配合，更多相亲相爱，把测试当成提高和推动质量的手段，不正应该是测试的方向吗？&lt;/div&gt;&lt;img src="http://www.cnblogs.com/guanhe/aggbug/2444996.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2010/12/15/agiletesting1.html</id><title type="text">敏捷测试专栏之一 《什么是敏捷软件测试》</title><summary type="text">在与不少测试从业人员讨论到敏捷的时候，被问得最多的大约是两个问题：“到底什么是敏捷软件测试？”，“敏捷软件开发还需要测试工程师吗？”。前一个问题是对于敏捷测试本身定义的疑问，第二个问题则是对敏捷开发将测试工程师排除在外的担心。其实，在探寻这两个问题答案的过程中，我们可以更清晰的了解敏捷软件开发中测试的工作定义，测试价值观，以及敏捷开发中开发与测试工程师的配合。鉴于这两个问题的意义，在本敏捷测试专栏的第一篇文章中，本人尝试从自己的实践出发，尽可能清楚的回答这两个问题。确实，相对于敏捷开发红遍大江南北的状况而言，对敏捷测试的讨论则低调得多。敏捷联盟定义了敏捷的4个价值声明，以及伴随的12条支持原则，这12条原则中没有一条单独提到测试。这是不是意味着测试在敏捷开发中并不重要呢？实际上，如果仔细研读敏捷的12个原则，以及各种不同的敏捷实践，就会发现，测试在敏捷开发中占有非常重要的地位。无论是原则中的“频繁交付”，还是对“可工作的软件”的度量，或是敏捷开发实践中的“测试驱动开发”，“行为驱动开发”，都离不开测试的支持。在本人看来，敏捷开发中不把测试单独拿出来描述的原因，恰恰是因为在敏捷开发中</summary><published>2010-12-15T09:02:00Z</published><updated>2010-12-15T09:02:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2010/12/15/agiletesting1.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2010/12/15/agiletesting1.html"/><content type="html">&lt;div&gt;&lt;span style="font-family: 'Times New Roman'; line-height: normal; font-size: medium; border-collapse: collapse; "&gt;&lt;p&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;br /&gt;本文已经首发于&lt;/span&gt;&lt;a href="http://www.infoq.com/cn" target="_blank" style="color: #0000cc; "&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000099; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;InfoQ中文站&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;，版权所有，原文为《XXX》，&lt;wbr&gt;如需转载，请务必附带本声明，谢谢。 &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.infoq.com/cn" target="_blank" style="color: #0000cc; "&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000099; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;InfoQ中文站&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;是一个面向中高端技术人员的在线独立社区，&lt;wbr&gt;为Java、.NET、Ruby、SOA、敏捷、&lt;wbr&gt;架构等领域提供及时而有深度的资讯、高端技术大会如&lt;/span&gt;&lt;a href="http://www.qconbeijing.com/" target="_blank" style="color: #0000cc; "&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000099; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;QCon&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; 、线下技术交流活动&lt;/span&gt;&lt;a href="http://www.infoq.com/cn/qclub/" target="_blank" style="color: #0000cc; "&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000099; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;QClub&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;、免费迷你书下载如&lt;/span&gt;&lt;a href="http://www.infoq.com/cn/architect/" target="_blank" style="color: #0000cc; "&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000099; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;《架构师》&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 10pt; font-family: Verdana; color: #000000; background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;等。&amp;#8203;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;div&gt;&lt;span style="border-collapse: separate; font-family: Lucida, 'Lucida Grande', Arial, 宋体, sans-serif; line-height: 16px; font-size: 10.8333px; "&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;在与不少测试从业人员讨论到敏捷的时候，被问得最多的大约是两个问题：&amp;#8220;到底什么是敏捷软件测试？&amp;#8221;，&amp;#8220;敏捷软件开发还需要测试工程师吗？&amp;#8221;。前一个问题是对于敏捷测试本身定义的疑问，第二个问题则是对敏捷开发将测试工程师排除在外的担心。其实，在探寻这两个问题答案的过程中，我们可以更清晰的了解敏捷软件开发中测试的工作定义，测试价值观，以及敏捷开发中开发与测试工程师的配合。鉴于这两个问题的意义，在本敏捷测试专栏的第一篇文章中，本人尝试从自己的实践出发，尽可能清楚的回答这两个问题。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;确实，相对于敏捷开发红遍大江南北的状况而言，对敏捷测试的讨论则低调得多。敏捷联盟定义了敏捷的4个价值声明，以及伴随的12条支持原则，这12条原则中没有一条单独提到测试。这是不是意味着测试在敏捷开发中并不重要呢？实际上，如果仔细研读敏捷的12个原则，以及各种不同的敏捷实践，就会发现，测试在敏捷开发中占有非常重要的地位。无论是原则中的&amp;#8220;频繁交付&amp;#8221;，还是对&amp;#8220;可工作的软件&amp;#8221;的度量，或是敏捷开发实践中的&amp;#8220;测试驱动开发&amp;#8221;，&amp;#8220;行为驱动开发&amp;#8221;，都离不开测试的支持。在本人看来，敏捷开发中不把测试单独拿出来描述的原因，恰恰是因为在敏捷开发中，测试不再是一个单独的、和开发独立的过程，而是变成了驱动开发、衡量产出的主要的手段，成为了敏捷开发中所有工程师在工作时必须时刻考虑和实践的一个部分。简而言之，敏捷软件测试更多的是一种理念，而非过程。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;既然是这样，为什么我们还要在这个专栏中专门来讨论&amp;#8220;敏捷软件测试&amp;#8221;？本人接触过不少软件开发和测试工程师，他们所处的组织有的正在努力向敏捷开发转型，有的已经实践了一段实践的敏捷开发，但由于由来已久的工作习惯，他们中的绝大多数并不能自觉的认识到测试在敏捷开发中的关键作用，而是有意无意的将测试仍然看作是与开发截然分开的&amp;#8220;下一个阶段&amp;#8221;，导致在实践敏捷开发的过程中遇到种种问题：要么是忽略了代码质量，导致在频繁的迭代过程中，每一个迭代的问题层出不穷；或是沿用原有的方法安排对系统的系统测试，导致测试团队疲于奔命，却总也赶不上开发所要求的进度。在这种情况下，专门来讨论敏捷软件开发中的测试，也就是敏捷软件测试的话题，对这些工程师应该会有一些帮助。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;那么，到底什么是敏捷软件测试？很难给敏捷测试下一个精确、完善的定义，在本人看来，接纳了敏捷的核心价值观（沟通，简单，反馈，勇气，尊重），在敏捷软件开发过程中开展的测试就可以被称作是敏捷软件测试。因此，敏捷软件测试并不是一个与敏捷软件开发同一层次的划分，而是敏捷软件开发中的一部分，与传统的测试不同，敏捷软件测试并不是一个独立的过程，相反，它与整个敏捷开发中的其他活动交织在一起，处处都能看到它的影子。由于敏捷软件测试并不倾向于一个单独的过程定义，本人拟从敏捷软件测试与传统测试观点的比较、敏捷软件测试中采用的方法、测试工程师在敏捷软件测试过程中的工作等方面来阐述之。在这篇文章中，我们主要从宏观的角度来描述敏捷软件测试，而在本专栏的后续文章中，我们将对敏捷软件测试中采用的方法、工程师在敏捷软件测试中的工作内容等进行进一步的描述。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;敏捷软件测试是建立在敏捷核心价值观的基础上，为了更生动的描述其与传统软件测试的区别，本人从自己的实践经验出发，尝试给出包含了本人认为包含了敏捷测试关键要素的&amp;#8220;敏捷测试检查表&amp;#8221;：&lt;/span&gt;&lt;/p&gt;&lt;table cellspacing="0" cellpadding="0" border="1px"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="83" valign="top" style="font-size: small; "&gt;&lt;p&gt;&lt;strong&gt;项目&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td width="290" valign="top" style="font-size: small; "&gt;&lt;p&gt;&lt;strong&gt;检查点&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td width="251" valign="top" style="font-size: small; "&gt;&lt;p&gt;&lt;strong&gt;注释&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width="83" valign="top" style="font-size: small; "&gt;&lt;p&gt;团队&lt;/p&gt;&lt;/td&gt;&lt;td width="290" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;测试工程师是否与开发工程师建立了紧密联系？&lt;/li&gt;&lt;li&gt;测试工程师是否与客户建立和紧密联系？&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td width="251" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;是否参加每日站立会议？是否与开发工程师可以展开随时的，面对面的，对等的讨论？&lt;/li&gt;&lt;li&gt;是否保持和客户的良好沟通？是否和客户一起维护良好定义的验收测试？&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width="83" valign="top" style="font-size: small; "&gt;&lt;p&gt;反馈&lt;/p&gt;&lt;/td&gt;&lt;td width="290" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;项目是否建立了合适的验收测试？&lt;/li&gt;&lt;li&gt;是否项目中每个人都能随时了解当前工作与可交付产品的距离？&lt;/li&gt;&lt;li&gt;是否建立了针对开发质量的度量标准？&lt;/li&gt;&lt;li&gt;开发工程师是否能够快速得到对提交代码的反馈？&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td width="251" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;使用Dashboard、燃尽图等方式展示当前工作与可交付产品之间的距离&lt;/li&gt;&lt;li&gt;建立单元测试覆盖率等度量指标&lt;/li&gt;&lt;li&gt;使用持续集成或频繁的构建让开发工程师快速得到提交代码的质量反馈&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width="83" valign="top" style="font-size: small; "&gt;&lt;p&gt;质量文化&lt;/p&gt;&lt;/td&gt;&lt;td width="290" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;是否建立了开发与测试工程师共享质量目标的原则？&lt;/li&gt;&lt;li&gt;团队是否注重开发质量，并在工作中尽可能保证高的开发/代码质量？&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td width="251" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;共享质量目标意味着质量责任由所有工程师共同承担&lt;/li&gt;&lt;li&gt;不仅关注最终的产出，不断对代码进行重构，保证代码质量&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width="83" valign="top" style="font-size: small; "&gt;&lt;p&gt;开发测试&lt;/p&gt;&lt;/td&gt;&lt;td width="290" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;是否进行了充分的开发测试？&lt;/li&gt;&lt;li&gt;是否设立了持续集成环境，并以持续集成的结果作为能够继续提交代码和发布的条件？&lt;/li&gt;&lt;li&gt;是否建立了足够多的自动化测试，以及在设计时关注自动化测试的要求？&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;td width="251" valign="top" style="font-size: small; "&gt;&lt;ul&gt;&lt;li&gt;开发测试应该建立一定的测试覆盖率标准，例如，在单元测试这个级别上，建立60%或80%的覆盖率要求&lt;/li&gt;&lt;li&gt;通过使用TDD、BDD等技术，保证产品和代码的可测试性&lt;/li&gt;&lt;li&gt;建立足够多的自动化测试，保证测试能够满足快速迭代的要求&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;检查表提到了&amp;#8220;团队&amp;#8221;、&amp;#8220;反馈&amp;#8221;、&amp;#8220;质量文化&amp;#8221;和&amp;#8220;开发测试&amp;#8221;四个方面的内容，在本人看来，这四个方面体现的就是敏捷软件测试与传统软件测试的最大的不同。传统软件测试关注的是通过尽可能完备的&amp;#8220;覆盖&amp;#8221;去发现尽可能多的问题，把测试和开发当成是两个独立的过程，测试是对开发阶段产生成果的&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;验证&lt;/strong&gt;。&lt;span style="font-size: 10pt; "&gt;而敏捷软件测试则建立了一种不同的质量文化：测试的目的是为了保证产品快速发布，也就是对生产率本身的提高。基于&amp;#8220;&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;验证&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;&amp;#8221;的出发点必然会要求测试与开发的独立，以及尽可能&amp;#8220;客观&amp;#8221;和&amp;#8220;完备&amp;#8221;的度量产品质量；而基于&amp;#8220;&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;生产率&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;&amp;#8221;的出发点则要求建立敏捷的团队，要求测试与开发尽可能紧密，要求建立具有高度可测试性的软件，以及基于这些的高度自动化测试。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;在检查表列出的所有项目中，&amp;#8220;质量文化&amp;#8221;是基础，&amp;#8220;团队&amp;#8221;是敏捷软件测试得以实施的条件，&amp;#8220;反馈&amp;#8221;和&amp;#8220;开发测试&amp;#8221;则是敏捷软件测试的具体方法。当然，你可以可以从敏捷的核心价值观来阶段这些项目：&amp;#8220;团队&amp;#8221;关注的是&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;沟通&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;与&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;尊重&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;；&amp;#8220;反馈&amp;#8221;直接对应于&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;反馈&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;；&amp;#8220;质量文化&amp;#8221;基于&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;勇气&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;（承担质量责任的勇气）与&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;尊重&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;；而&amp;#8220;开发测试&amp;#8221;则是&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;反馈&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;与&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;简单&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;的具体体现。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;另一个在本文最初提出来的问题是：&amp;#8220;敏捷软件开发还需要测试工程师吗？&amp;#8221;，对这个问题，业界有不同的观点。有人认为需要，因为总有一些是需要测试工程师的技能完成的工作；当然，也有人认为不需要，因为敏捷开发中的测试注重开发测试与自动化测试，开发工程师就可以自己搞定与测试相关的工作。在实践中，那些大规模实践敏捷开发的公司（例如Google），倾向于在组织中设置数量较少的测试工程师，在项目中分配较少的测试资源，甚至对某些项目，完全不使用测试工程师。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt; "&gt;就我的个人实践经验，对大部分的项目，尤其是为明确的&lt;/span&gt;&lt;strong style="font-size: 10pt; "&gt;客户&lt;/strong&gt;&lt;span style="font-size: 10pt; "&gt;开发的项目，需要在敏捷开发团队中设置专职的测试工程师，因为：&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-size: 10pt; "&gt;测试与开发具有不同的思维方式：测试更注重全面的验证和检查一个系统，而开发工程师往往很难在大的范围内建立这样的思维方式。因此，无论是从系统的层面验证产品，或是从应用系统的角度发现值得测试和验证的点（access point），专职的测试工程师都更有效。&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: 10pt; "&gt;专职的测试工程师能够更关注于测试基础，建立测试需要的基础架构：由于测试工程师具有更好的对测试的理解，通常他们能够更多的考虑测试的需求而开发适合项目的测试基础架构（自动化测试框架），而开发工程师可以使用这些框架来建立面向功能或代码的测试。&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="border-collapse: separate; font-family: Lucida, 'Lucida Grande', Arial, 宋体, sans-serif; line-height: 16px; font-size: 10pt; "&gt;但是，不得不说的是，敏捷开发对开发和测试工程师都提出了更要的要求，尤其是对测试工程师而言，传统的只能&amp;#8220;精确模拟用户操作&amp;#8221;的测试工程师，因为不能为产品带来生产率的提升，在敏捷开发的团队中，很难有所作为。在本专栏的后续文章中，我们会进一步讨论测试工程师在敏捷软件开发中的工作和任务。&lt;/span&gt;&amp;nbsp;&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/guanhe/aggbug/1907019.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/guanhe/archive/2010/12/15/agiletesting1.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2010/11/15/1878034.html</id><title type="text">对Singleton类解依赖</title><summary type="text">上周六在杭州的《互联网软件测试大会》上做演讲的时候提到可测试性的话题，顺手举了一个例子：“B类存在对A类的依赖，A类是一个Singleton类，B类使用到了A类中的一个需要被mock的函数，这种情况下如何处理？” 本来，这是《修改代码的艺术》上曾经提到的一个典型模式，该书中有非常详细的描述。但今天收到某位听众的邮件，问这个问题应该怎么回答。 正好好久没有写过blog了，于是顺手写了一段简单的代码来说明这个问题。</summary><published>2010-11-15T15:14:00Z</published><updated>2010-11-15T15:14:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2010/11/15/1878034.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2010/11/15/1878034.html"/><content type="html">&lt;p&gt;上周六在杭州的《互联网软件测试大会》上做演讲的时候提到可测试性的话题，顺手举了一个例子：&amp;#8220;B类存在对A类的依赖，A类是一个Singleton类，B类使用到了A类中的一个需要被mock的函数，这种情况下如何处理？&amp;#8221; 本来，这是《修改代码的艺术》上曾经提到的一个典型模式，该书中有非常详细的描述。但今天收到某位听众的邮件，问这个问题应该怎么回答。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;正好好久没有写过blog了，于是顺手写了一段简单的代码来说明这个问题。&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;类Singleton是一个单件类，该类实现了一个名为getUserName()的函数，getUserName函数需要连接到数据库并从数据库中取出用户名称。类DependsOnSingleton类是被测类，该类中的sayHello函数使用到了Singleton类的getUserName函数，在对DependsOnSingleton类进行单元测试的时候，显然我们并不想设置一个真正的数据库，所以最理想的情况是使用mock方式得到一个可以被控制的Singleton类的getUserName函数的实现。如果Singleton类不是一个单件类，这件事情非常简单，使用接口提取的方法从Singleton类提取接口，然后就可以使用mock技术了。但由于Singleton是一个单件类，这件事情就比较棘手了：Singleton.getInstance会返回一个由getInstance自身逻辑决定的Singleton对象，这样就没法直接塞入一个mock对象了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;代码：&lt;/strong&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('3ee4c89f-bd00-4b03-afd5-6e44251e3127')"&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_3ee4c89f-bd00-4b03-afd5-6e44251e3127" style="display:none" alt="" /&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" class="code_img_opened" id="code_img_opened_3ee4c89f-bd00-4b03-afd5-6e44251e3127" onclick="cnblogs_code_hide('3ee4c89f-bd00-4b03-afd5-6e44251e3127',event)"&gt;&lt;div id="cnblogs_code_open_3ee4c89f-bd00-4b03-afd5-6e44251e3127"&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: #008080;"&gt;&amp;nbsp;1&lt;/span&gt;&amp;nbsp;&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;Singleton&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;2&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton&amp;nbsp;_instance;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;3&lt;/span&gt;&amp;nbsp;&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;final&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;String&amp;nbsp;userName&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;Dennis&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;4&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;5&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton()&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;6&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;7&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;8&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton&amp;nbsp;getInstance()&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;9&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;if&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;==&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_instance)&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;10&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;_instance&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;11&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;12&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_instance;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;13&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;14&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;15&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;getUserName()&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;throws&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;16&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;&amp;nbsp;Connect&amp;nbsp;to&amp;nbsp;DB&amp;nbsp;and&amp;nbsp;return&amp;nbsp;data&amp;nbsp;from&amp;nbsp;DB&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;17&lt;/span&gt;&amp;nbsp;&lt;span style="color: #008000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;&amp;nbsp;Throw&amp;nbsp;Exception&amp;nbsp;instead&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;18&lt;/span&gt;&amp;nbsp;&lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&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;throw&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception(&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;There's&amp;nbsp;no&amp;nbsp;DB&amp;nbsp;exist&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;19&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;20&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&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: #008080;"&gt;1&lt;/span&gt;&amp;nbsp;&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;DependsOnSingleton&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;2&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;3&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;4&lt;/span&gt;&amp;nbsp;&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;String&amp;nbsp;sayHello()&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;throws&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;5&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;Hi,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;+&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton.getInstance().getUserName();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;6&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;7&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&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;#8220;分离&amp;#8221;的问题。在Singleton情况下，由于getInstance函数不受控导致了这样的情况，所以最简单的做法是为Singleton类增加一个setTestingInstance函数，使用这个函数注入一个受控的实例。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;修改后的Singleton：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('9ce39e5f-1558-410f-a24f-068c43744ed8')"&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_9ce39e5f-1558-410f-a24f-068c43744ed8" style="display:none"  alt="" /&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" class="code_img_opened" id="code_img_opened_9ce39e5f-1558-410f-a24f-068c43744ed8" onclick="cnblogs_code_hide('9ce39e5f-1558-410f-a24f-068c43744ed8',event)"&gt;&lt;div id="cnblogs_code_open_9ce39e5f-1558-410f-a24f-068c43744ed8"&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: #008080;"&gt;&amp;nbsp;1&lt;/span&gt;&amp;nbsp;&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;Singleton&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;2&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;static&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton&amp;nbsp;_instance;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;3&lt;/span&gt;&amp;nbsp;&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;final&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;String&amp;nbsp;userName&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;Dennis&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;4&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;5&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton()&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;6&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;7&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;8&lt;/span&gt;&amp;nbsp;&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;static&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton&amp;nbsp;getInstance()&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;9&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;if&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;==&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_instance)&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;10&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;_instance&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Singleton();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;11&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;12&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&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;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_instance;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;13&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;14&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;15&lt;/span&gt;&amp;nbsp;&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;static&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;setTestingInstance(Singleton&amp;nbsp;instance)&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;16&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_instance&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;instance;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;17&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;18&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;19&lt;/span&gt;&amp;nbsp;&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;String&amp;nbsp;getUserName()&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;throws&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;20&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;&amp;nbsp;Connect&amp;nbsp;to&amp;nbsp;DB&amp;nbsp;and&amp;nbsp;return&amp;nbsp;data&amp;nbsp;from&amp;nbsp;DB&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;21&lt;/span&gt;&amp;nbsp;&lt;span style="color: #008000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;&amp;nbsp;Throw&amp;nbsp;Exception&amp;nbsp;instead&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;22&lt;/span&gt;&amp;nbsp;&lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&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;throw&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception(&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;There's&amp;nbsp;no&amp;nbsp;DB&amp;nbsp;exist&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;23&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;24&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;测试代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('8373d73a-b286-4a22-86ab-b0c7cb5a5155')"&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" class="code_img_closed" id="code_img_closed_8373d73a-b286-4a22-86ab-b0c7cb5a5155" style="display:none"  alt="" /&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" class="code_img_opened" id="code_img_opened_8373d73a-b286-4a22-86ab-b0c7cb5a5155" onclick="cnblogs_code_hide('8373d73a-b286-4a22-86ab-b0c7cb5a5155',event)"&gt;&lt;div id="cnblogs_code_open_8373d73a-b286-4a22-86ab-b0c7cb5a5155"&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: #008080;"&gt;&amp;nbsp;1&lt;/span&gt;&amp;nbsp;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;junit.framework.Assert;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;2&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;junit.framework.TestCase;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;3&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;4&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;org.easymock.EasyMock;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;5&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;6&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&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;TestDependsOnSingleton&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;extends&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;TestCase&amp;nbsp;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;7&lt;/span&gt;&amp;nbsp;&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;void&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;testSayHello()&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;throws&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Exception{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;8&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton&amp;nbsp;depend&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;EasyMock.createMock(Singleton.&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;&amp;nbsp;9&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton.setTestingInstance(depend);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;10&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;11&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EasyMock.expect(depend.getUserName()).andReturn(&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;Dennis&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;12&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EasyMock.replay(depend);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;13&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;14&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DependsOnSingleton&amp;nbsp;itu&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;DependsOnSingleton();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;15&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.assertEquals(&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;Hi,&amp;nbsp;Dennis&lt;/span&gt;&lt;span style="color: #000000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;itu.sayHello());&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;16&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;EasyMock.verify(depend);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;17&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;18&lt;/span&gt;&amp;nbsp;&lt;span style="color: #000000;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/guanhe/aggbug/1878034.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/guanhe/archive/2010/11/15/1878034.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2010/05/20/1740149.html</id><title type="text">我们为什么要使用开源测试工具？</title><summary type="text">为什么要使用开源测试工具？作为一个开源测试工具的推崇者，我经常被问到这个问题。许多测试工程师对商业测试工具情有独钟，总觉得商业测试工具既好用又强大，而开源测试工具功能弱，缺陷多，而且不好用。对开源测试工具的偏见一方面来自于商业测试工具的宣传，另一方面，也来自部分测试工程师在使用开源测试工具过程中的心态。在本人所在的组织中，公司内使用的绝大多数测试工具或多或少都有开源测试工具的影子，从开源测试工具在本组织的应用中看来，使用开 源测试工具带来的优势非常明显…</summary><published>2010-05-20T08:23:00Z</published><updated>2010-05-20T08:23:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2010/05/20/1740149.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2010/05/20/1740149.html"/><content type="text">为什么要使用开源测试工具？作为一个开源测试工具的推崇者，我经常被问到这个问题。许多测试工程师对商业测试工具情有独钟，总觉得商业测试工具既好用又强大，而开源测试工具功能弱，缺陷多，而且不好用。对开源测试工具的偏见一方面来自于商业测试工具的宣传，另一方面，也来自部分测试工程师在使用开源测试工具过程中的心态。在本人所在的组织中，公司内使用的绝大多数测试工具或多或少都有开源测试工具的影子，从开源测试工具在本组织的应用中看来，使用开 源测试工具带来的优势非常明显…</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2010/01/08/1642301.html</id><title type="text">在第四届软件质量年会上的演讲（视频与PPT）</title><summary type="text">在第四届软件质量年会上的演讲，标题是”让测试敏捷起来“。 下面的链接是InfoQ上的视频和PPT：http://www.infoq.com/cn/presentations/duannian-agile-test</summary><published>2010-01-08T07:59:00Z</published><updated>2010-01-08T07:59:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2010/01/08/1642301.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2010/01/08/1642301.html"/><content type="text">在第四届软件质量年会上的演讲，标题是”让测试敏捷起来“。 下面的链接是InfoQ上的视频和PPT：http://www.infoq.com/cn/presentations/duannian-agile-test</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2009/11/14/1603133.html</id><title type="text">敏捷测试感悟（之二）</title><summary type="text">在本系列的第一部分中，我们简要回顾了敏捷开发，以及敏捷测试与传统测试的不同。在第一部分中，我们特别提到，敏捷测试的要点之一就是，不依据于角色而是依据于任务来考虑整个开发过程中的测试。但是，对一个开发组织来说，组织中一定存在开发工程师和测试工程师的角色划分，作为一个敏捷团队中的测试工程师，他的主要工作职责是什么呢？或者说，他可以在哪些工作上发挥自己的作用呢？</summary><published>2009-11-14T13:19:00Z</published><updated>2009-11-14T13:19:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2009/11/14/1603133.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2009/11/14/1603133.html"/><content type="text">在本系列的第一部分中，我们简要回顾了敏捷开发，以及敏捷测试与传统测试的不同。在第一部分中，我们特别提到，敏捷测试的要点之一就是，不依据于角色而是依据于任务来考虑整个开发过程中的测试。但是，对一个开发组织来说，组织中一定存在开发工程师和测试工程师的角色划分，作为一个敏捷团队中的测试工程师，他的主要工作职责是什么呢？或者说，他可以在哪些工作上发挥自己的作用呢？</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2009/11/06/1597628.html</id><title type="text">敏捷测试感悟（之一）</title><summary type="text">Agile testing（敏捷测试）基本上是伴随着敏捷开发的概念成长起来的，但在受关注程度上，远远不及敏捷开发本身。自然，开发队伍从数量和活跃度上来讲大于测试队伍，是其中的一个原因；除了这个原因之外，“敏捷测试究竟如何在项目中发挥作用”这个问题可能也是导致敏捷测试概念的流行度远远不如敏捷开发的原因之一。在敏捷环境中工作了几年之后，对敏捷测试有了一些感悟，希望和大家分享。</summary><published>2009-11-06T11:03:00Z</published><updated>2009-11-06T11:03:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2009/11/06/1597628.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2009/11/06/1597628.html"/><content type="text">Agile testing（敏捷测试）基本上是伴随着敏捷开发的概念成长起来的，但在受关注程度上，远远不及敏捷开发本身。自然，开发队伍从数量和活跃度上来讲大于测试队伍，是其中的一个原因；除了这个原因之外，“敏捷测试究竟如何在项目中发挥作用”这个问题可能也是导致敏捷测试概念的流行度远远不如敏捷开发的原因之一。在敏捷环境中工作了几年之后，对敏捷测试有了一些感悟，希望和大家分享。</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2009/06/01/1493789.html</id><title type="text">《Google API大全：编程·开发·实例》一书将在本周末的GDD（Google开发者大会）上首发</title><summary type="text">我也是本书的作者之一，虽然只在其中占了两章的内容：）《Google API大全：编程·开发·实例》这本书是国内的第一本较为完整的介绍Google API的书，内容囊括了所有主要的Google API，并用大量的实例展示了Google API的应用方法。在这本书的首页上，我选择了“Google是一种生活方式”作为我对这本书的推荐语。“Google是一种生活方式”，此言不虚，现在的网名，或多或少的被搜索引擎影响着使用Internet的方式，Google在其中的贡献不言而喻。当然，对我而言，“Google是一种生活方式”更有所指，Google的产品一向以良好的编程接口著称，对于以技术工作为主的工程师们，基于Google的产品构建自己的产品，更是一件值得一试的事情。《Google API大全：编程·开发·实例》作者序：</summary><published>2009-06-01T07:46:00Z</published><updated>2009-06-01T07:46:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2009/06/01/1493789.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2009/06/01/1493789.html"/><content type="text">我也是本书的作者之一，虽然只在其中占了两章的内容：）《Google API大全：编程·开发·实例》这本书是国内的第一本较为完整的介绍Google API的书，内容囊括了所有主要的Google API，并用大量的实例展示了Google API的应用方法。在这本书的首页上，我选择了“Google是一种生活方式”作为我对这本书的推荐语。“Google是一种生活方式”，此言不虚，现在的网名，或多或少的被搜索引擎影响着使用Internet的方式，Google在其中的贡献不言而喻。当然，对我而言，“Google是一种生活方式”更有所指，Google的产品一向以良好的编程接口著称，对于以技术工作为主的工程师们，基于Google的产品构建自己的产品，更是一件值得一试的事情。《Google API大全：编程·开发·实例》作者序：</content></entry><entry><id>http://www.cnblogs.com/guanhe/archive/2009/03/11/1408595.html</id><title type="text">4月份到上海出差</title><summary type="text">4月份到上海出差，有机会的话希望能够和上海的测试同行们见见面：）不知道有没有感兴趣的朋友？我到上海会住在南京路附近。 </summary><published>2009-03-11T03:39:00Z</published><updated>2009-03-11T03:39:00Z</updated><author><name>关河</name><uri>http://www.cnblogs.com/guanhe/</uri></author><link rel="alternate" href="http://www.cnblogs.com/guanhe/archive/2009/03/11/1408595.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/guanhe/archive/2009/03/11/1408595.html"/><content type="text">4月份到上海出差，有机会的话希望能够和上海的测试同行们见见面：）不知道有没有感兴趣的朋友？我到上海会住在南京路附近。 </content></entry></feed>
