TDD测试驱动开发︱一个完整的TDD测试驱动开发演练案例
2022-11-06
来源:逸言 我是张逸
定是正确的。
在编写第二个测试时,由于测试样本与之前的测试完全不一样,之前的简单实现就不能满足新增的测试了。事实上,测试就是要去验证实现逻辑,这其中最重要的测试目标就是分支。不同的分支可能会返回不同的结果,如果我们根据分支来设计测试,就能有效保障实现的正确性。这称为“三角测试法”。
常见问题:
没有将测试代码看做是代码的一部分。当编写多个测试方法时,没有及时重构;例如,应及时将game对象与actualAnswer对象提取为字段,以避免不必要的声明。
直接暴露表达式,而未对表达式进行方法提取,以表达业务意义;
guess()方法过长;应该通过提取方法来改进代码的可读性;
Game类与Answer类的职责分配不合理,将Answer类设计为仅具有get()和set()的数据对象,而将判断数值是否正确、位置是否正确的逻辑分配给了Game。没有考虑get()和set()是否真正有必要;如果我们对guess()方法进行了方法提取,可以识别出代码的坏味道“Feature Envy”,即Game的方法用到的都是Answer的属性。这时,应该采用移动方法的重构手法对其进行重构。
开始第二个任务
我们选择的第二个任务为“随机生成答案”,这是一个独立的职责。编写测试类时,很容易驱动出AnswerGenerator类。关键在于,我们该如何编写单元测试来验证生成的结果。我们对结果的要求是:
数字必须是0…9之间;
产生的四个数字不能相同;
讨论:究竟由谁来承担“随机生成答案”的职责?
学员容易将此职责直接分配给Answer。然而,随机生成答案与创建一个答案适用于不同的场景,这对于Answer的调用者而言,并不友好。尤其对于只需要答案的场景,还需要无端地引入对随机数的依赖,显然是不合理的。
编写测试方法的过程与前相似,仍然按照Given-When-Then模式来编写(若测试方法比较简单,可以不遵循这一模式,但思考的过程却应该按照该模式)。
在编写then部分的测试时,可能出现疑问。
问题:如何验证生成的答案是否正确?
我们已经将答案建模为Answer,因此AnswerGenerator的generate()方法要返回的对象类型为Answer。那么,我们怎么知道返回的Answer对象是合法的呢?一种做法是获取Answer的属性,然后再进行验证。那么,为了测试的验证而暴露这些属性,是否适合?
要完成对答案正确性的验证,直接暴露答案的属性是不妥当的,至少目前没有获取答案属性的需求。我们的做法是定义一个验证方法。这是否仍然属于为测试而定义行为的做法呢?这个问题有点像鸡与鸡蛋的哲学问题。我们应该还原到设计,看看这种手法是否改善了设计,如此即可。毕竟,这种对答案正确性的校验,也可以说是业务逻辑的一种。
说明:在开始编写“检查输入是否合法”任务时,你会发现,这里所谓多余的验证,就会派上用场。
这个验证方法可以是单纯的返回true或者false,但从需求来看,这个返回结果并没有很好地展现验证要求:究竟是因为数字超出了范围,还是出现了相同的数字?我个人更倾向于用自定义异常来表示生成的答案违背了这两条规则。因此,我们可以为Answer定义一个validate()方法,以验证生成的Answer是否满足规则要求;如果不符合,就抛出对应的异常。
知识:JUnit中对异常的验证
随着JUnit版本的演化,先后提供了三种验证异常的机制。
一种是传统的在测试代码中通过编写try... catc
免责声明:
1、IT项目管理界发布的所有资讯与文章是出于为业界传递更多信息之目的,并不意味着赞同其观点或证实其描述。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请浏览者仅作参考,并请自行核实相关内容。
2、本站部分内容转载于其他网站和媒体,版权归原作者或原发布媒体所有。如文章涉及版权等问题,请联系本站,我们将在两个工作日内进行删除或修改处理。敬请谅解!