假设我正在编写游戏2048(http://gabrielecirulli.github.io/2048/)的克隆,我想写一个测试来验证"正确的事情"当比赛被赢得时,就会发生#34;假设我的游戏状态封装在一个类中,并且状态本身是私有的。
我想我可以编写代码来玩游戏,通过公共界面进行评估,当我即将获胜然后获胜时;然而,这似乎有点矫枉过正。我宁愿设置游戏状态,进行获胜动作并验证对象的行为是否符合预期。
设计此类测试的推荐方法是什么?我目前的想法是,测试应该是该类的公共成员函数,或者测试基础结构应该由类进行测试。这两个看起来都很令人反感。
编辑:回答第一个问题:我假设在这个例子中我没有设置游戏状态的方法,并且没有理由写一个;因此,它只是为了编写测试而添加额外的功能......然后需要另一个成员函数来测试,获得游戏状态函数。那么我就要编写至少两个公共方法并进行测试以编写这一个测试。更糟糕的是,这些方法基本上打破了封装,如果内部细节发生变化,我必须更改这两种方法及其测试,除了进行测试之外别无其他原因。这似乎比提供测试功能更令人厌恶。
答案 0 :(得分:3)
首先,请记住,测试驱动开发是design-oriented methodology。测试的主要目标是影响SUT及其合作者的设计;其他一切都是为了骑行。
其次,TDD强调小步骤。肯特贝克在他的书Test-Driven Development: By Example中说:
如果你需要花费一百行为一个断言创建对象,那么就会出现问题。您的对象太大,需要拆分。(p.194)
这意味着你应该听听你对编写赢得游戏过度杀伤所必需的代码的直觉。
你也说过:
我想改为设置游戏状态,进行获胜动作并验证对象是否符合预期。
完全你应该做什么。
为什么呢?因为您正在测试最终游戏场景。导致最终游戏的大部分/全部细节都无关紧要 - 你只是想确保程序“正确的事情......当游戏获胜时”。因此,这些是与您的测试相关的唯一细节。
那么这些与您的测试相关的细节是什么?要想出来,有助于与a colleague进行讨论。
问:测试如何配置系统以表明游戏已赢得而不实际玩游戏?
答:说出游戏已经获胜的事情。
通过提出这些问题,我们已经弄清楚了设计的一些细节。具体来说,我们已经确定了一个可以在OOP by an interface中表示的角色。
这个“裁判”角色会是什么样子?也许:
(伪代码)
begin interface Referee
method GameHasBeenWon returns boolean
end interface
接口的存在建立a seam in the design,允许测试使用test-double来代替生产对象。不仅如此,它还允许更改此功能的实现(例如,影响游戏如何被确定为“赢得”的规则更改),而无需修改任何周围的代码。
这与您提到的其他内容直接相关:
我假设在这个例子中我没有设置游戏状态的方法,并且没有理由写一个;因此,只是为了编写测试而添加额外的功能......
测试是您代码的使用者。如果测试难以与您的代码交互,那么生产代码(具有更多约束)进行交互将更加困难用它。这就是"Listening to your tests"的含义。
请注意,有很多可能的设计可能会脱离TDD。每个开发人员都会有自己的偏好,这会影响架构的外观和感觉。主要的内容是TDD有助于将您的程序分成许多小块,这是core tenets of object oriented design之一。