上周六在杭州的《互联网软件测试大会》上做演讲的时候提到可测试性的话题,顺手举了一个例子:“B类存在对A类的依赖,A类是一个Singleton类,B类使用到了A类中的一个需要被mock的函数,这种情况下如何处理?” 本来,这是《修改代码的艺术》上曾经提到的一个典型模式,该书中有非常详细的描述。但今天收到某位听众的邮件,问这个问题应该怎么回答。
正好好久没有写过blog了,于是顺手写了一段简单的代码来说明这个问题。
问题:
类Singleton是一个单件类,该类实现了一个名为getUserName()的函数,getUserName函数需要连接到数据库并从数据库中取出用户名称。类DependsOnSingleton类是被测类,该类中的sayHello函数使用到了Singleton类的getUserName函数,在对DependsOnSingleton类进行单元测试的时候,显然我们并不想设置一个真正的数据库,所以最理想的情况是使用mock方式得到一个可以被控制的Singleton类的getUserName函数的实现。如果Singleton类不是一个单件类,这件事情非常简单,使用接口提取的方法从Singleton类提取接口,然后就可以使用mock技术了。但由于Singleton是一个单件类,这件事情就比较棘手了:Singleton.getInstance会返回一个由getInstance自身逻辑决定的Singleton对象,这样就没法直接塞入一个mock对象了。
代码:

代码 1 public class Singleton {
2 static private Singleton _instance;
3 public final String userName = "Dennis";
4
5 private Singleton() {
6 }
7
8 static Singleton getInstance() {
9 if(null == _instance) {
10 _instance = new Singleton();
11 }
12 return _instance;
13 }
14
15 String getUserName() throws Exception{
16 // Connect to DB and return data from DB
17 // Throw Exception instead
18 throw new Exception("There's no DB exist");
19 }
20 }
1 public class DependsOnSingleton {
2
3 ...
4 public String sayHello() throws Exception {
5 return "Hi, " + Singleton.getInstance().getUserName();
6 }
7 }
解决:
其实解决这个问题的思路,按照《修改代码的艺术》一书的说法,主要是解决“分离”的问题。在Singleton情况下,由于getInstance函数不受控导致了这样的情况,所以最简单的做法是为Singleton类增加一个setTestingInstance函数,使用这个函数注入一个受控的实例。
修改后的Singleton:

代码 1 public class Singleton {
2 static private Singleton _instance;
3 public final String userName = "Dennis";
4
5 private Singleton() {
6 }
7
8 public static Singleton getInstance() {
9 if(null == _instance) {
10 _instance = new Singleton();
11 }
12 return _instance;
13 }
14
15 public static void setTestingInstance(Singleton instance) {
16 _instance = instance;
17 }
18
19 public String getUserName() throws Exception{
20 // Connect to DB and return data from DB
21 // Throw Exception instead
22 throw new Exception("There's no DB exist");
23 }
24 }
测试代码:

代码 1 import junit.framework.Assert;
2 import junit.framework.TestCase;
3
4 import org.easymock.EasyMock;
5
6 public class TestDependsOnSingleton extends TestCase {
7 public void testSayHello() throws Exception{
8 Singleton depend = EasyMock.createMock(Singleton.class);
9 Singleton.setTestingInstance(depend);
10
11 EasyMock.expect(depend.getUserName()).andReturn("Dennis");
12 EasyMock.replay(depend);
13
14 DependsOnSingleton itu = new DependsOnSingleton();
15 Assert.assertEquals("Hi, Dennis", itu.sayHello());
16 EasyMock.verify(depend);
17 }
18 }
为什么要使用开源测试工具?
作为一个开源测试工具的推崇者,我经常被问到这个问题。许多测试工程师对商业测试工具情有独钟,总觉得商业测试工具既好用又强大,而开源测试工具功
能弱,缺陷多,而且不好用。对开源测试工具的偏见一方面来自于商业测试工具的宣传,另一方面,也来自部分测试工程师在使用开源测试工具过程中的心态。
在本人所在的组织中,公司内使用的绝大多数测试工具或多或少都有开源测试工具的影子,从开源测试工具在本组织的应用中看来,使用开
源测试工具带来的优势非常明显:
- 极低的License费用:这个是显而易见的一个优点。设想,如果公司需要对Web应用进行上万并发的性能测试,使用LoadRunner等商业
测试工具的费用绝对不是一个小数字;
- 更高的集成度:大多数商业测试工具本身也号称提供了自己的“完整解决方案”,但商业测试工具往往只能覆盖测试中一部分的领域,对于集成测试或是针
对应用的接口测试方面,商业测试工具很难提供企业需要的好的解决方案。这样一来,这部分企业自己建立的自动化测试工具就很难被集成到商业测试工具形成的
“测试框架”中。而采用开源测试工具解决方案的话,这个问题根本就不是问题;
- 更适合企业需要:出于商业利益的考虑,商业测试工具总是试图覆盖“最大的用户群体”,因此商业测试工具往往是那种“谁都可以用”,但“在哪里都不
是特别好用”的那一类工具,反之,开源测试工具在这方面具有显然的优势;
- 更适合提高企业的测试技术水平:许多开源测试工具中都体现和非常值得学习的测试思想和方法,由于开源本身的特性,这些思想和方法是非常容易通过对
开源测试工具的研究来进行学习和掌握的。
以JMeter这个工具为例,我在许多场合下向测试工程师推荐这个性能测试工具,的确也有一些测试工程师尝试了这个工具,但从他们那里,我得到的反
馈往往是:“为什么这个工具的界面这么难看?”,“为什么这个工具没有xx功能(与商业测试工具相比)?”,“为什么这个工具没有漂亮的文档?”。许多人
在第一印象上便认为,这个工具比不上商业测试工具,然后就弃之如敝屐。实际上真是这样吗?我所在的组织的性能测试几乎完全依赖于JMeter,通过在
JMeter上扩展的图表功能,支持集群等功能,通过少量的代码,JMeter可以生成比商业测试工具更加漂亮,更加有价值的图表,而且,更重要的
是,100%适合我们自己的环境需要。
当然,除了看到开源测试工具的优点,我们也应该看到开源测试工具的不便之处。与商业测试工具相比,开源测试工具在产品的用户交互性,易用性,易学习
性方面显然
不是那么好(当然,在我看来,这方面不是测试工具的重点)。因此,要在组织中使用和引入开源测试工具的话,对组织中的成员,组织环境是有一定的要求的。
就本人的经验而言,许多目前的开源测试工具,例如Mantis、Testlink、JMeter、Selenium/Webdriver、
xUnit等都已经是非常成熟的测试工具,拥有了大量的使用者,也有许多成功的应用实例,本人的实践已经充分证明了这些工具在实际工作中能够带来的收益:
即使只是简单的使用开源测试工具去完成某个特定的任务,或是用来搭建公司内部的测试管理平台,也能从这些工具中受益不少;更何况开源测试工具拥有众多的开
发者,处于不断的完善和提高中,具有良好的扩展性,给你充分修改和改造的自由,从这个角度来说,如果你拥有足够的资源,想要打造属于自己的测试平台,开源
测试工具绝对是一个好的平台。
在第四届软件质量年会上的演讲,标题是”让测试敏捷起来“。
下面的链接是InfoQ上的视频和PPT:
让测试敏捷起来
在本系列的第一部分中,我们简要回顾了敏捷开发,以及敏捷测试与传统测试的不同。在第一部分中,我们特别提到,敏捷测试的要点之一就是,不依据于角色而是依据于任务来考虑整个开发过程中的测试。
但是,对一个开发组织来说,组织中一定存在开发工程师和测试工程师的角色划分,作为一个敏捷团队中的测试工程师,他的主要工作职责是什么呢?或者说,他可以在哪些工作上发挥自己的作用呢?
敏捷过程中与测试相关的任务很多,概括说来有如下一些:
- 建立不同级别的测试验收标准(也就是test suite),包括单元测试、集成测试、系统测试等各个层面的验收标准;
- 推动整个组织的质量文化,保证整个组织的成员在质量责任与目标方面达成一致;
- 通过技术或是管理的手段,保证产品、代码具有良好的可测试性;
- 通过自动化测试手段缩短每个产品发布周期中测试所需的时间;
- 与客户沟通确认客户可接受的软件质量标准,并建立针对此标准的验收测试;
- 深入了解应用系统和业务需求,通过探索性测试方法设计有效的测试用例,发现产品中的缺陷;
- 建立对整个团队可见的质量度量体系,保证整个团队能够随时看到产品的质量度量值。
这些工作都可以是敏捷团队中测试工程师角色的工作任务,但显然,在现实中,不太可能要求所有这些工作都由测试工程师来承担─同时,让测试工程师承担全部这些工作任务也并不合理,某些工作由开发工程师角色,或是由开发工程师和测试工程师共同承担更为合理。
接下来,我更详细的把列出的这7项工作中非常成“测试工程师必须完成的工作”,“测试工程师需要去推进的工作”,以及“能为项目带来巨大价值的工作”。
- 测试工程师必须完成的工作:
- 与客户沟通确认客户可接受的软件质量标准,并建立针对此标准的验收测试;
- 深入了解应用系统和业务需求,通过探索性测试方法设计有效的测试用例,发现产品中的缺陷;
- 建立系统和用户验收级别的测试验收标准;
- 通过自动化测试手段缩短每个产品发布周期中测试所需的时间;
- 测试工程师需要去推进的工作:
- 推动低层次测试(单元测试和接口级别的测试)的进行。低层次是测试对于需要快速发布的敏捷开发来说至关重要,但在大部分国内的软件组织中,开发测试都需要经过不断的推动才能被最终执行下去,测试工程师具有相关的测试技巧,并且能够用翔实的统计数据表明低层次测试的必要性,是最好的推动低层次测试的人选;
- 推动整个组织的质量文化,保证整个组织的成员在质量责任与目标方面达成一致。这一点对于敏捷组织来说也是至关重要的;在许多传统的软件开发组织中,经常能看到开发和测试之间的扯皮,针对一个遗漏的缺陷,大家考虑第一件事不是如何去修复,如何去防止,而是首先“追究责任”─在敏捷过程中,需要一个非常不同的环境:也即,质量责任是由所有工程师共同承担的,对于出现的问题,重要的是解决和预防。
- 能为项目带来巨大价值的工作
- 建立对整个团队可见的质量度量体系,保证整个团队能够随时看到产品的质量度量值。保证产品质量度量的可见可以让整个团队清楚的看到我们现在正在工作的产品与我们期望交付的产品在质量上还有多大的差距,这样整个团队可以以质量度量作为指示,集中精力工作在这些差距上,从而可以尽快的发布“可工作”的产品。“验收测试的通过度”,“单元测试的通过率”,“功能完成情况”等等项目都可以是质量度量中的度量项。
- 通过技术或是管理的手段,保证产品、代码具有良好的可测试性。良好的可测试性对于产品的维护,自动化测试的进行都是非常有利的─我觉得,甚至可以武断的说,如果一个应用系统没有良好的可测试性,就很难期望可以在该项目上设置良好的测试框架。测试工程师一般不参与具体的设计工作和代码实施工作,但测试工程师可以推动开发工程师在设计和实现时尽可能的考虑可测试性设计,另一方面,测试工程师也可以通过测试覆盖率这个质量及时发现应用中可测试性不强的地方,推动开发工程师的改进。
综上,就是我个人认为的敏捷过程中的测试角色必须,应该和可以进行的工作。当然,要完成这些工作,和传统的QA工程师相比,敏捷过程对测试工程师提出了更好的技能上的要求。在我看来,一个敏捷项目中的测试工程师应该具有这样一些技能:
- 良好的沟通和协作能力;
- 良好的设计和代码能力,至少可以和开发工程师在同一水平上讨论具体的设计和代码实现;
- 快速学习和总结的能力(运用探索性测试发现缺陷);
- 对自动化测试有深刻的理解(至少要能清楚的认识到自动化测试不等于UI自动化测试,也不等于用自动化测试工具进行录制和回放);
- 快速的风险分析和判断能力(在许多情况下都不会有足够的时间开展full regression,如何判断风险和决定相应的对策至关重要)。
==========================分隔线============================
第二部分完结。
第三部分我打算探讨下敏捷测试中的测试过程管理。与传统的软件测试过程不同,敏捷测试过程是一个非常简单的过程模型,我的重点会放在敏捷测试过程管理中的一些实用工具上。
另外,可测试性貌似也是一个很好的话题,不知道有没有对这个话题感兴趣的朋友?
摘要: Agile testing(敏捷测试)基本上是伴随着敏捷开发的概念成长起来的,但在受关注程度上,远远不及敏捷开发本身。自然,开发队伍从数量和活跃度上来讲大于测试队伍,是其中的一个原因;除了这个原因之外,“敏捷测试究竟如何在项目中发挥作用”这个问题可能也是导致敏捷测试概念的流行度远远不如敏捷开发的原因之一。关于敏捷测试,我能找到的较早的比较系统化的描述文档应该是2002年的这...
阅读全文
摘要: 4月份到上海出差,有机会的话希望能够和上海的测试同行们见见面:)不知道有没有感兴趣的朋友?我到上海会住在南京路附近。
阅读全文