首先,我很早就意识到TDD似乎不适合游戏开发.看来这个问题的观点差异很大.我最初没有受过教育的意见是,TDD似乎对所有游戏逻辑都有效.我认为任何处理视频输出和声音的内容都会被抽象为可视化测试的类.
事情开始很顺利.目标是创建一个2d太空飞行游戏(小行星为那些关心的人).我为Ship类创建了一系列单元测试.像初始化,旋转这样的东西很容易在一系列中测试,例如:GetRotation(),TurnRotateRightOn(),Update(1),GetRotation(),Expect_NE(rotation1,rotation2).然后我遇到了第一个问题.
我对TDD的理解是你应该写测试你认为应该如何使用这个类.我希望船能够移动,所以我写了一个基本上说的课程. GetCoordinates(),ThrustOn(),GetCoordinates().那可以确保船在某处移动.但是,我很快意识到我必须确保船舶以正确的方向和正确的速度移动.接下来是一个75线单元测试,我基本上必须初始化旋转,检查坐标,初始化推力,更新船,获得新坐标,检查新的旋转.更重要的是,我认为没有必要在游戏中获得船的速度(只是坐标,船应该更新自己).因此,我没有直接获得速度的方法.所以测试基本上必须重新计算速度应该是什么,所以我可以确保它与更新后得到的坐标相匹配.总而言之,这是非常混乱,非常耗时,但工作.测试失败,测试通过等.
直到后来我才意识到我想将船的更新代码重构为抽象的“Actor”类时,这很好.我意识到虽然Actor的每个子类都需要能够正确计算一个新位置,但并不是每个子类都必须以相同的方式更新它们的速度(一些碰撞,一些不碰撞,一些碰撞静态速度).现在,我基本上面临着重复和改变庞大而庞大的测试代码的前景,我不禁想到应该有更好的方法.
有没有人有经验处理单元测试这种复杂的黑盒子类型的工作?看起来我基本上不得不在测试中编写完全相同的物理代码,所以我知道结果应该是什么.这真的是自我失败,而且我确信我一路上都错过了这一切.我非常感谢任何人提供的任何帮助或建议.
在生成包含在测试中的预期输出时,重要的是要高度确信这些预期结果是正确的.因此,需要最小化生成预期结果所需的代码量.特别是,如果
你发现自己编写的测试脚手架几乎和测试中的组件一样复杂,那么出现测试本身的bug的前景就成了一个严重的问题.
在这种情况下,我将直接从运动方程生成测试数据.我使用Mathematica来实现这个目的,因为我可以直接输入方程式,解决它们,然后生成结果的图形和表格.这些图表让我可以看到结果,从而确信它们是可靠的. Excel / OpenOffice / Google Apps可用于相同的目的,以及像Sage这样的Mathematica的开源替代品.无论选择什么,关键的问题是能够解决运动方程而无需编写非平凡的代码.
一旦我们有一组好的测试用例以及预期的结果,我们就可以对单元测试进行编码.请注意,测试代码非常简单,本身不执行任何计算.它只是将组件的输出与我们之前获得的硬编码结果进行比较.在测试用例到位后,我们可以编写组件本身,添加代码直到测试全部通过.当然,在严格的TDD中,这些动作恰好按此顺序发生.我承认,我并不亲自坚持瀑布,而是倾向于在创建测试数据,编写测试和编写组件代码之间来回反弹.
Mathematica或Excel文档本身在初始创建测试之后具有使用寿命.添加新功能时可以再次使用它们,或者(天堂禁止)以后可以找到错误.我主张像处理源代码一样处理文档.
在本练习结束时,结果是一个“防弹”组件,我们确信自己将在任何给定的控制输入集合下计算对象的正确位置和方向.在此基础上,我们可以构建使用该功能的更多组件,例如船只,小行星,碟子和镜头.为了避免每个组件的测试用例的组合爆炸,我将偏离严格的黑盒测试方法.因此,例如,如果我们测试“ship”组件,我们会编写测试,知道它使用位置/方向组件.使用这种白盒知识,我们可以避免重新测试与运动相关的所有角落情况.船舶单元测试可以执行“烟雾测试”,以验证船舶实际上是否响应控制输入,但主要重点是测试船舶部件本身特有的任何功能.
所以,总结一下:
>使位置/方向计算成为单个组件的责任>使用外部工具为一组全面的测试用例生成数据,从而高度确信数据是正确的>避免测试本身的复杂计算逻辑>使用白盒知识躲避组件层次结构中测试用例的组合爆炸