本文是在读了《Working Effectively with legacy Code 》第九章,关于在无法将类放入测试用具中时遇到的四种最为常见的问题:
(1)无法轻易创建该类的对象。
(2)当该类位于测试用具中时,测试用具无法轻易通过编译构建。
(3)我们需要用到的构造函数具有副作用。
(4)构造函数中有一些要紧的工作,我们需要感知到它们。
这四个问题在进行单元测试或者接口测试的时候,会对测试工作造成很大的阻碍,这就是一个代码可测性的问题。当遇到这样的问题的时候,有两种方法,第一、强行构建一个类去完成测试,但是这会造成测试的时候大部分工作都耗费在构建这样一个类的过程中;第二、重构代码,使代码具有可测性。本文将通过书中的列子来简单介绍一下如何提高代码的可测性。
如在一个计费系统中,我们有一个未测试的Java类:CreditValidator。
public class CreditValidator
{
public CreditValidator (RGHConnection connection,
CreditMaster master,
String validatorID) {
}
Certificate validateCustomer(Customer customer) throws InvalidaCredit{
}
public class RGHConnection
{
public RGHConnection(int port, String Name, String passwd) throws IOException {
}
}
我们可以看到CreditValidator构造函数含有三个参数RGHConnection,CreditMaster,validatorID。其中RGHConnection对象在构造时会连接到一个服务器,这个链接被用来从服务器上获取必要的信息,以检查客户的余额。
宁一个类CreditMaster,则提供一些我们在检查余额的过程中会用到的策略信息。该类的构造函数会从一个文件中加载相关信息,并把这些信息保存在内存中以备后用。
如果按照我们开头讲的强制构造一个类来完成测试,如下所示:
public void testCreate() throws Exception {
RGHConnection connection = new RGHConnection(DEFAULT_PORT,"admin","rii8ii9s");
CreditMaster master = new CreditMaster ("crm2.mas",true);
CreditValidator validator = new CreditValidator(connection,master,"a");
}
虽然我们构建一个这个样的类,但是你能忍受这个测试的速度,根据《Working Effectively with legacy Code 》书中提到大于1秒的单元测试,都不叫单元测试。因此在测试中建立到服务器的连接并不是一个好的主意。首先其好事就比较长,况且服务器也并不总是处于服务状态。可想而知RGHConnection是一个令人恼火的参数。我们的设想是:若能创建某种伪造的RGHConnection对象并使CreditValidator相信它是一个真正的RGHConnection的话,就可以避开所有链接的问题了。
首先来看一下RGHConnection所拥有的方法:
RGHConnection
+ RGHConnection(port,name,password)
+ connect()
+ disconnect()
+RFDIReportFor(id:int):RFDIReport
+ACTIOReportFor(customerID:int) ACTIOReport
+retry()
+fromPacket():RFPacket
看上去RGHConnection中有一些方法是用来处理与连接相关的任务的:如connect、disconnect以及retry。另外还有一些业务方法。因此如果要伪造一个RGHConnection对象的话,那么这个伪造的对象也必须拥有这些方法也能提供一样的信息。
在这些条件下,伪造一个RGHConnection对象最好的方法是对RGHConnection类应用接口提取。如果你手头一个支持重构的工具,那么它很可能也会支持接口提取方法。我们来看一下接口提取后的情况:
<interface>
IRGHConnection
+connect()
+ disconnect()
+RFDIReportFor(id:int):RFDIReport
+ACTIOReportFor(customerID:int) ACTIOReport
由于retry()和fromPacket()不属于业务相关方法因此只需要在实现类中增加这两个方法,至此我们可以轻松的构建出一个FackeConnection类,并使它能够提供我们所需要的反馈信息,然后将这个伪造的对象用在测试中:
public class FakeConnection implements IRGHConnection
{
public RFDIReport report;
public void connect() {}
public void disconnnect(){}
public RFDIReport RFDReportFor(int id) {return report;}
public ACTIOReport ACTIOReportFor(int customerID) {return null;}
}
下面我们来写测试
void testNoSuccess()throws Exception{
CreditMaster master = new CreditMaster("crm2.mas",true);
IRGHConnection connection = new FakeConnection();
CreditValidator validator = new CreditValidator(connection,master,"a");
connection.report = new RFDReport(....);
Certificate result = validator.validatorCustomer(new Customer(...));
assertEquals(Certificate.VALID,result.getStatus());
}
虽然FakeConnection类看起来有点奇怪:它的方法要么是空的要么就简单的返回null。这种情形并不常见。更糟的是,它有一个任何人都可以看到的并随意设置的公共变量。这样一个类似似乎违反了所有的良好准则。但你要看到,实际上并非如此。对于一个用来使得测试可行的类,规则是所不同的。FakeConnection中的代码并非产品代码。它永远也不属于最终投入运行的应用,而只是为了测试工具和测试而诞生。
有了这个fake类我们接下去便可以做更多的相关测试。这就是提高代码可测试性和遇到教难构造的的类的时候所采取的一种方法。若代码设计阶段就将RGHConnection设计为接口,那么在后面的测试中会是测试更加方便,使代码在后期的重构也会更加方便。
分享到:
相关推荐
在软件开发过程中,软件测试是不可或缺的一环,它关乎产品的质量和稳定性。本文主要探讨了软件测试自动化的一些具体实践和策略,对于提升测试效率和优化软件开发流程具有重要意义。 首先,针对带有图形界面的软件...
2. 软件测试的目标:测试的目标是发现问题、修复bug,提高软件产品的质量和可靠性。 3. 软件质量的定义:软件质量是指软件产品符合用户期望的程度,包括功能、易用性、可靠性等方面。 4. 质量管理的重要性:质量...
- **解析**: 单元测试主要针对软件中的单一模块进行测试,目的是发现并修复代码级别的错误。测试依据通常是模块的详细设计文档,测试方法通常采用白盒测试技术,这意味着测试人员会根据内部逻辑结构设计测试案例。 ...
- **引入现代技术和工具**:使用现代开发工具和技术,如版本控制、持续集成等,提高代码质量和管理效率。 - **重构与优化**:简化代码结构和逻辑,消除冗余,提升性能。 - **培训与知识共享**:组织内部培训,...
选择软件测试行业是因为它的发展前景广阔,随着技术进步和用户对产品质量要求的提高,测试人员的需求持续增长。 【软件开发与测试过程】软件开发通常包括架构设计、开发编码、测试和维护等阶段。在这个过程中,角色...
16. **良好需求的属性**:明确性、完整性、一致性、可测试性、可追踪性和现实可行性。 17. **QA经理与项目经理的职责区别**:QA经理专注于质量保证,而项目经理负责项目的时间、成本和范围管理。 18. **质量努力的...
- **详细解释**:关键是确保测试用例覆盖所有的功能点,并且具有可执行性和可重复性。 ### 40. 测试计划的目的 - **核心知识点**:测试计划的核心目的。 - **详细解释**:制定测试计划的目的是为了指导测试活动的...
1. “只要程序能运行就可以了,写单元测试浪费时间”:这种看法忽略了单元测试在提高代码质量和维护性方面的重要性。 2. “代码很简单不需要单元测试”:简单代码同样存在错误的可能,且随着时间推移,代码可能会变...
- 持续集成:优化设计并提高代码的可维护性。 - 代码重构:不断迭代,快速响应需求变化。 - 敏捷开发:保持高度灵活性,适应快速变化的市场和技术环境。 #### 第2章 软件需求分析 - **需求分析过程**: - 需求...
外包测试不仅仅是简单的代码检查,而是涵盖了一整套系统性的质量保证活动。因此,企业需要具备熟练掌握测试工具、具备良好外语能力和学习能力的专业人员。然而,目前市场上这类人才相对匮乏,企业需要与高等教育机构...
将源代码拆分成小文件是提高代码可读性和可维护性的常见做法。根据描述,这个分文件版的SQLite 3.8.2源代码已经按照原始的结构和顺序进行了拆分,这应该会让开发者更容易地定位和理解代码。 在Visual Studio 2012...
最后一章,作者讲述了单元测试和重构,强调它们在保证软件质量、持续改进设计以及提高代码适应性方面的作用。 “第二部分:编写坚实代码”则侧重于SOLID原则。SOLID是五个面向对象设计原则的首字母缩写,分别是: 1...
6. **社区讨论**:开源项目往往伴随着社区的讨论,该项目可能包含了一些关于泛型的讨论记录,如GitHub issue或论坛帖子,反映了社区对泛型的看法和建议。 通过参与这样的开源项目,开发者不仅可以了解到Go泛型的...
3. **代码复用**:合理地设计接口和类,遵循单一职责原则,提高代码的可重用性和可维护性。 4. **性能优化**:理解JVM内存模型,合理使用集合类型,避免不必要的对象创建,使用StringBuilder代替String进行字符串...
它可以帮助我们降低代码间的耦合度,提高代码的可读性和可维护性。通常,依赖注入可以通过三种方式实现: 1. **Setter注入**:通过setter方法将依赖项注入到对象中。 2. **构造器注入**:在对象构造时通过构造函数...