测试驱动开发(转)

大千世界中唯一缺乏的就是人类的注意力。
                                                                  —凯文·凯利

一.测试驱动的好处:

1.  程序中每一项功能都有测试来验证它的操作的正确性。无论何时我们因疏忽破坏了某些已有的功能,它就会告诉我们。因此我们可以向程序增加功能,或者更改程序结构,而不用担心在这个过程中会破坏重要的东西。

 

2.  首先篇写测试可以迫使我们使用不同的观点。我们必须从程序调用者的有利视角去观察我们将要测试的程序。这样,我们就会在关注程序的功能的同时,直接关注它的接口。通过首先编写测试,我们就可以设计出便于调用的软件。

 

3.  因为要迫使自己的程序变成可方便测试的,所以它必须和周边环境解耦。

 

4.  测试可以作为一种文档形式。如果想知道如何调用一个函数或者创建一个对象,会有一个测试展示给你看。它帮助其他程序员了解如何使用代码。

 

5.  通过编写这部分代码的测试用例,对其功能的分解、使用过程、接口都进行了设计。而且这种从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。

 

6.  当然测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug是很多开发人员的梦想。针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。

 

二.原理

测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。

 

三.过程

1) 明确当前要完成的功能。可以记录成一个 TODO 列表。

2) 快速完成针对此功能的测试用例编写。

3) 测试代码编译不通过。

4) 编写对应的功能代码。

5) 测试通过。

6) 对代码进行重构,并保证测试通过。

7) 循环完成所有功能的开发。

为了保证整个测试过程比较快捷、方便,通常可以使用测试框架组织所有的测试用例。一个免费的、优秀的测试框架是 Xunit 系列,几乎所有的语言都有对应的测试框架。

 

.原则

测试隔离。不同代码的测试应该相互隔离。对一块代码的测试只考虑此代码的测试,不要考虑其实现细节(比如它使用了其他类的边界条件)。

一顶帽子。开发人员开发过程中要做不同的工作,比如:编写测试代码、开发功能代码、对代码重构等。做不同的事,承担不同的角色。开发人员完成对应的工作时应该保持注意力集中在当前工作上,而不要过多的考虑其他方面的细节,保证头上只有一顶帽子。避免考虑无关细节过多,无谓地增加复杂度。

测试列表。需要测试的功能点很多。应该在任何阶段想添加功能需求问题时,把相关功能点加到测试列表中,然后继续手头工作。然后不断的完成对应的测试用例、功能代码、重构。一是避免疏漏,也避免干扰当前进行的工作。

测试驱动。这个比较核心。完成某个功能,某个类,首先编写测试代码,考虑其如何使用、如何测试。然后在对其进行设计、编码。

先写断言。测试代码编写时,应该首先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。

可测试性。功能代码设计、开发时应该具有较强的可测试性。其实遵循比较好的设计原则的代码都具备较好的测试性。比如比较高的内聚性,尽量依赖于接口等。

及时重构。无论是功能代码还是测试代码,对结构不合理,重复的代码等情况,在测试通过后,及时进行重构。关于重构,我会另撰文详细分析。

小步前进。软件开发是个复杂性非常高的工作,开发过程中要考虑很多东西,包括代码的正确性、可扩展性、性能等等,很多问题都是因为复杂性太大导致的。极限编程提出了一个非常好的思路就是小步前进。把所有的规模大、复杂性高的工作,分解成小的任务来完成。对于一个类来说,一个功能一个功能的完成,如果太困难就再分解。每个功能的完成就走测试代码-功能代码-测试-重构的循环。通过分解降低整个系统开发的复杂性。这样的效果非常明显。几个小的功能代码完成后,大的功能代码几乎是不用调试就可以通过。一个个类方法的实现,很快就看到整个类很快就完成啦。本来感觉很多特性需要增加,很快就会看到没有几个啦。你甚至会为这个速度感到震惊。比如一个类A使用了类BC,就应该编写到A使用BC功能的测试代码前,完成对BC的测试和开发。那么是不是每个小类或者小函数都应该测试哪?我认为没有必要。你应该运用你的经验,对那些可能出问题的地方重点测试,感觉不可能出问题的地方就等它真正出问题的时候再补测试吧。

. 怎么编写测试用例

  • 操作过程尽量模拟正常使用的过程。
  • 全面的测试用例应该尽量做到分支覆盖,核心代码尽量做到路径覆盖。
  • 测试数据尽量包括:真实数据、边界数据。
  • 测试语句和测试数据应该尽量简单,容易理解。
  • 为了避免对其他代码过多的依赖,可以实现简单的桩函数或桩类(Mock Object)。
  • 如果内部状态非常复杂或者应该判断流程而不是状态,可以通过记录日志字符串的方式进行验证。

六.一个测试优先设计的示例

public void testMove( )

{

WumpusGame g = new WumpusGame( );

g.connect(4,5,”E”);

g.setPlayerRoom(4);

g.east( );

assertEquals(5,g.getPlayerRoom( ));

}

按程序执行步骤可以断定会得到assertEquals里的结果。如果以后我们想知道如何把两个房间连接起来,或者如何朝一个特定的方向移动,这个测试就会直截了当地展示给我们如何去做。

 

转载于:https://www.cnblogs.com/SweetDream/archive/2005/11/11/274316.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值