测试,驱动开发(Test-Driven) Development

fastpoint 2003-09-18 12:43:54
测试,驱动开发》英文名(Test-Driven) Development,是一本很不错的影印版书籍,多日来读的颇有心得,抽出里面比较精华的内容献给论坛上所有从事类单元测试的人,大家相互学习。

XP的核心理论就是“测试,驱动开发”,如果使用XP过程在没有测试的保障情况下,那真是一种可怕的灾难,大家有兴趣可以看看一些关于“盖鸡窝”的介绍文章。

《测试,驱动开发》开始使用了一个例子,实际上此书的一半都是围绕这个例子在讲解如何增量的开展类测试过程,这个例子是这样的,
作者提供了一个Money类,这个Money类代表着世界上所有的货币,当然现实生活中,不同国家的货币兑换是有汇率的,例如:CHF和USD的换算是2:1,那么5个USD和10个CHF相加等于10个USD。

好的,按照中国程序员的习惯肯定就直接书写Money的实现代码了,不,这在XP过程中是个错误,大家要养成测试-实现的新思路,作者首先书写Money的测试类,下面是Money测试类的一个片断:

Public void testMultiplication{
Dollar five = new Dollar(5);
five.times(2);
assertEquals(10,five.amount);
}

该测试片断属于应用Junit的测试代码(大家有兴趣可以访问Junit的官方站点http://www.junit.org),我可以为感兴趣的读者逐行解释。

Public void testMultiplication{},这是使用Junit展开类测试的标准规定,所有使用Junit的测试方法必须符合一下条件(摘自我的书籍):

1.测试方法必须是公有的(Public)。
2.测试方法必须被声明为(Void)。
3.测试方法的前置名词必须是test。
4.测试方法无任何传参。

assertEquals(10,five.amount),这是Junit提供的断言方法,用来检测左边用户设定的期望值和右边程序实际输出值是否正确,值得一提的是Junit有两种错误方式,如果Junit抛出Failer,那么这个错误就是被测试类的错误,如果Junit抛出Error,那么这个错误属于构造测试类本身的错误。
...全文
52 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
swinging 2003-09-24
  • 打赏
  • 举报
回复
引用:
别人说爱是一点点给的甜蜜,那么测试也是一点点增加的细致

好恰当有趣的比喻
中国有句古话:心急吃不了热豆腐

一点一点构造测试,保持小步前进,以确保每一步的正确,
我觉得是关键,
在《重构》一书中也有这样的忠告。
swinging 2003-09-24
  • 打赏
  • 举报
回复
支持下。

确实,我有时候写了很多的测试,
开始都很好,
突然有一次重构把类的结构做了本质的改动,
修改了接口,

结果测试类完全不能支持了,
需要调整大量的代码,好麻烦(没什么难度,就是麻烦,我怕麻烦,^_^)。
比如说,我修改了类的构造器,使用了几个个状态对象来代替原来的一堆数据的初始化,之所以不使用原来的构造器是因为觉得没有必要而且难以被使用,这样的构造器公开出来无疑是自找麻烦,
需要修改很多代码来重新支持,由原来的直接使用数据构造该类改为先生成状态对象再构造,要准备的数据不少,所以修改很多,要花不少时间,因为构造的测试用例已经很多了(比如说30来个),
让我很为难,
后来干脆维护一个Deprecated的构造器来保证测试用例的运行。

很多时候会考虑重构的目标到底是什么,模式?
呵呵,我很多时候的目标是让以后以最小的代码量来完成现在的重复工作,
因为实践中实在太多东西都是拷贝粘贴一下,然后修修改改来用,
所以减少这些工作,很多时候成了我的重构目标。sweat

而测试,是支持重构的基石。
努力ing
fastpoint 2003-09-24
  • 打赏
  • 举报
回复
看来CSDN的技术论坛也不过如此,本想发贴和大家讨论测试技术问题的,数天来只有一位仁兄回贴,不知道这个论坛平常叽叽喳喳的“高手”到哪里去了?
fastpoint 2003-09-23
  • 打赏
  • 举报
回复
好的,作者这个时候开始为测试程序增加了一些分量,显然他又要开始“驱动开发”了。

最新的测试程序片断:
Public void testMultiplication{
Dollar five = new Dollar(5);
Dollar product = five.times(2);//改变的
assertEquals(10,product.amount);//改变的

product.times(3);//改变的
assertEquals(15,product.amount);//改变的
}

作者新增了一个名为product的Dollar对象来“容纳”five,这段程序是无法被编译通过的,原因出在“Dollar product = five.times(2);”语句上,因为times方法是一
个空返回值得方法,它看起来有点像这样:

void times(int Multiplication){
amount = amount * Multiplication;
return null;//这是想象的,呵呵。
}

现在,作者的目的又达到了,他要开始重构原来的代码了,他要把times方法改变成为一个可以返回Dollar对象的方法了:

改变前:
void times(int Multiplication){
amount = amount * Multiplication;
}

改变后:
Dollar times(int Multiplication){
return new Dollar(amount * Multiplication);
}
fastpoint 2003-09-23
  • 打赏
  • 举报
回复
别人说爱是一点点给的甜蜜,那么测试也是一点点增加的细致,现在Dollar类经过重构,其内部组织已经发生了变化,那么针对它的原来的测试程序也需要调整一下了,说是增量也可以的。

调整前的测试程序片断:
Public void testMultiplication{
Dollar five = new Dollar(5);
five.times(2);
assertEquals(10,five.amount);
}

调整后的测试程序片断:
Public void testMultiplication{
Dollar five = new Dollar(5);
five.times(2);
assertEquals(10,five.amount);

five.times(3);//增加的
assertEquals(15,five.amount);//增加的
}
fastpoint 2003-09-18
  • 打赏
  • 举报
回复
我们回转到《测试,驱动开发》书籍的内容上来,作者在给出了Money类的测试类部分片断后,用灰底给出了一段文字:

USD5 + CHF10 = USD10,if rate is 2:1(如果汇率是2比1)
USD5 * 2 = USD10
Make "amount" Private
Dollar side-effects?
Money rounding?

实际上作者要我们明白,因为测试类中使用了“five.times(2);
”和“five.amount”,我们在实现Money类的时候必须加入这两个设计,在类中实现汇率换算方法和提供存储Money数量的私有变量,作者也提到了四个现在所没有拥有的东西:

1.No Class Dollar(没有测试类)
2.No constructor(没有构造方法)
3.No method times(int)(没有汇率换算方法)
4.No field amount(没有存储Money数量的私有变量)

好的,我们来看看作者怎样实现Money类:

class Dollar{

int amount;

Dollar(int amount){}

void times(int Multiplication){}
}

接下来可以使用Junit对已经存在的Money类进行测试了,Junit抛出了一个异常:Junit.framework.AssertionFailedError:expected:<10> but was:
<0>

这个异常正是我们所想要的,因为我们实现的Dollar类的构造方法和汇率方法都是空的。

实际上解决这个异常是非常简单的,我们只要为Dollar类的amount赋值10,就可以成功的欺骗junit,让它出现令人愉悦的“绿条”(Junit中,绿条代表测试成功,红条代表测试失败)。

后面书的作者给出了一些继续测试下去的列表:
1.Add a little test.(增加一点点的测试,实际上这本书的测试例子就是在不断一点点增长的)。
2.Run all tests and fail.(改变了就要全部重新测试,以发现错误为目的)。
3.Make a little change.(做一些小小的改变,其实这就是重构动作的开始)。
4.Run the tests and succeed.(重构后再次展开测试,直到Junit能证明你所做的重构是正确的)。
5.Refactor to remove duplication.(删除重构前的脚本)。

后面的作者罗里罗唆一堆大实话,我也有兴趣罗里罗唆讲给大家听,其实他的思维逻辑就是这样的:

1.int amount = 10;//我们用这个欺骗了Junit
2.int amount = 5*2;//这是废话,但是作者说2是一个货币汇率基数
3.去除直接为int amount 赋值10,这不道德,笑。
4.在void times(int Multiplication){}方法空体中加入“amount = 5*2”,本来这就是一个货币自带的换算方法,因该让它承担一些责任。
5.times方法有个参数,这样的话它又变成了void times(int Multiplication){amount = amount * Multiplication;}
6.继续,最后的Dollar类进化成这样:

class Dollar{

int amount;

Dollar(int amount){
this.amount = amount;
}

void times(int Multiplication){
amount = amount * Multiplication;
}
}

times方法现在总算成了能接受货币汇率换算基数的好方法了。

5,227

社区成员

发帖
与我相关
我的任务
社区描述
软件工程/管理 质量管理/软件测试
功能测试压力测试安全性测试 个人社区 湖南省·长沙市
社区管理员
  • 软件测试
  • 虫无涯
  • 小博测试成长之路
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎大家加入到软件测试的社区,在这里,希望大家勇于发表自己的看法,欢迎大家分享自己在软件测试工作过程中遇到的问题以及工作经验分享。

1.想转行的小伙伴,遇到问题没有及时回复的,可以私聊小博进行反馈

2.大家对社区有好的建议,都可以在社区发帖进行反馈

推荐大家学习的软件测试入门笔记:软件测试入门学习笔记

试试用AI创作助手写篇文章吧