单元测试的正交性与单元测试的简洁性


14

我正在为视频游戏的转向系统编写单元测试。系统具有多种行为(由于原因A避免了该区域,由于原因B避免了该区域,每个行为都向该区域的地图添加了一些上下文。然后,一个单独的函数解析该地图并产生所需的运动。

我在决定如何编写行为的单元测试时遇到了麻烦。正如TDD所建议的,我只对行为如何影响所需的运动感兴趣。例如,避免-原因-A会导致移动远离建议的不良位置。我实际上并不在乎行为如何或为何为地图添加上下文,仅是所需的运动远离该位置。

因此,我对每种行为的测试都建立了行为,将其写入地图,然后执行地图解析功能来计算出所需的动作。如果该运动满足我的要求,那么我很高兴。

但是,现在我的测试取决于行为是否正常运行以及映射解析功能是否正常运行。如果解析功能失败,那么我将获得数百个失败的测试,而不是几个。许多测试写作指南都建议这是一个坏主意。

但是,如果我通过模拟映射直接针对行为的输出进行测试,那么我肯定与实现紧密结合了吗?如果我可以通过使用稍微不同的行为从地图上获得相同的期望运动,则测试应该仍然可以通过。

所以现在我正遭受认知失调。构造这些测试的最佳方法是什么?


...不确定是否有一个神奇的答案。基本上,您会测试可能会破坏的事物。因此,理想情况下,您将以某种方式神奇地知道高级测试已经足以覆盖哪些低级测试,而他们不需要自己的低级单元测试。另一种看待它的方式是:从测试失败到开发人员解决问题为止,将花费多少时间?您希望这段时间很短。如果您没有任何单元,而只是完善的功能测试(全面的高水平覆盖),那时间将仍然太长。尝试将其用作启发式指南。
Calphool 2014年

Answers:


10

在理想的世界中,您确实会拥有一组完全正交的单元测试,而这些测试均处于同一抽象级别。

在现实世界中,您通常会在应用程序的许多不同级别上进行测试,因此较高级别的测试通常会使用已经由专用较低级别的测试进行测试的功能。(许多人更喜欢将这种更高级别的测试称为子系统/集成测试,而不是单元测试;尽管如此,它们仍然可以在相同的单元测试框架上运行,所以从技术角度来看并没有太大区别。)

我不认为这是一件坏事。关键是要让代码测试适合项目和情况的最佳方式,而不要遵循“理想方式”。


我想作者的主要观点是您应该对这些更高级别的测试使用存根/模拟还是真正的实现
SiberianGuy

2

这种测试就是发明模拟的原因。主要思想是:为对象(地图,行为,角色等)编写一个模拟,然后使用该模拟而不是实际对象编写测试。人们有时称嘲笑存根,我相信两者都有别的意思。

在您的情况下,您每当需要测试行为时就为该映射编写一个模拟,而每当您要测试该地图时就为行为编写其他模拟。理想情况下,您的模拟将比您模拟的实际行为简单得多,仅合并测试实际需要的方法或变量。您可能必须为每个测试编写一个不同的模拟,或者您可以重用一些模拟。无论如何,它们应该适合于测试,并且不应尝试尽可能地类似于实际行为。

如果您包含一些地图或行为的示例,也许有人可以提供您可以编写的模拟示例。不是我,因为我从未编程过比Pong更高级的视频游戏,即使那时我也在读一本书,但也许有人精通单元测试和游戏开发。


0

我认为您尝试测试的东西比一个单元要高得多。

大多数行为测试都需要在其背后提供一些智能,以判断其行为是否正确。使用自动化测试很难做到这一点。


这就是为什么我提到简洁。我可以很容易地编写小型单元测试来测试行为中的各个代码行,并且已经完成了,但是这取决于读取映射解析函数的输出。我的行为测试没有一个很大,每个测试仅测试行为的一项功能。
tenpn 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.