TDD测试驱动开发︱一个完整的TDD测试驱动开发演练案例
2022-11-06
来源:逸言 我是张逸
如这里的Game类为被测类,则测试类命名为GameTest。
方法命名规则:测试方法应表述业务含义,这样就能使得测试类可以成为文档。测试方法可以足够长,以便于清晰地表述业务。为了更好地辨别方法名表达的含义,ThoughtWorks提倡用Ruby风格的命名方法,即下划线分隔方法的每个单词,而非Java传统的驼峰风格。建议测试方法名以should开头,此时,默认的主语为被测类。例如:
这里的方法可以阅读为:Game should return 0A0B when no number guessed correctly。显然,这是一条描述了业务规则的自然语言。
现在编写测试。由于事先已经明确被测类为Game,编写测试的Given部分,让我们思考如何创建Game对象?是否可以简单地创建?
分析任务,需要判断猜测结果,则必然要求获知游戏的答案。这个答案与Game的关系是什么呢?这里产生的驱动力是如何创建Game对象?为了创建该对象,需要提供哪些准备?这使得我们驱动出Answer类的定义。
讨论:由4个数字组成的答案是否需要封装?
学员容易写出的代码,以如下方式表现答案(Answer):
整数数组
整数类型的可变参数
字符串
第一种方式除了缺乏对整数值的限制外,一个问题还在于暴露了实现细节。第二种方式甚至无法对答案的个数进行限制。第三种方式则与输入有关,使得Game类还要承担解析输入字符串的职责,违背了单一职责原则(说明:在后面,我们为Answer类提供了工厂方法,可以将传入的字符串解析为Answer对象,也即是由Answer承担解析输入字符串的职责,这同时也遵循“信息专家模式”。)
思考:Answer的定义
我们可以从如何构造一个Answer对象着手,看看该如何定义Answer类。
知识:单一职责原则
由Robert Martin提出,该原则指出:就一个类而言,应该只专注于做一件事和仅有一个引起变化的原因。
编写When可以帮助开发者思考类的行为。一定要从业务而非实现的角度去思考接口。例如:
实现角度的设计:check()
业务角度的设计:guess()
注意两个方法命名表达意图的不同。
编写Then实际上是考虑如何验证。没有任何验证的测试不能称其为测试。由于该任务为判断输入答案是否正确,并获得猜测结果,因而必然需要返回值。从需求来看,只需要返回一个形如xAxB的字符串即可。
思考:是否需要将猜测结果封装为类?
至少就目前而言,并没有必要。因为从需求来看,仅仅需要返回一个形如xAxB的字符串而言。这是需要遵循简单设计的要求,不必过度设计。
如前所述,任务“判断每次的猜测结果”存在多个测试样本,例如一个都不对,或者全部正确,又或者值正确而位置不正确等,因而需要编写多个测试。在编写第一个测试时,可以简单实现使得测试快速通过,然后随着多个测试的编写,再驱动出检查输入数值的算法。
根据以上的分析,我们编写的第一个测试如下所示,它遵循了Given-When-Then模式:
这个测试已经驱动出了Answer的创建,Game类的定义,guess()接口的定义。在保证编译通过后,应该首先运行该测试。此时测试必然是失败的。为了使该测试快速通过,我们可以简单实现guess()方法,例如直接返回“0A0B”字符串。接着,就可以编写第二个测试。
思考:为何要先运行一个失败的测试?
首先,它能够保证测试框架是没有问题的;其次,它可以避免偶然的成功,因为测试通过不等于实现一
免责声明:
1、IT项目管理界发布的所有资讯与文章是出于为业界传递更多信息之目的,并不意味着赞同其观点或证实其描述。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请浏览者仅作参考,并请自行核实相关内容。
2、本站部分内容转载于其他网站和媒体,版权归原作者或原发布媒体所有。如文章涉及版权等问题,请联系本站,我们将在两个工作日内进行删除或修改处理。敬请谅解!