【问题标题】:Is unit testing viable in game programming?单元测试在游戏编程中可行吗?
【发布时间】:2010-03-10 17:01:04
【问题描述】:

我很喜欢单元测试的想法,但我在将它应用到游戏编程时遇到了麻烦。游戏是高度有状态的,并且代码通常不会将自己分解为不同的单元。根据我的经验,大多数函数都会改变状态而不是返回值。

考虑像playerJump(height) 这样的简单操作。我很想有一个测试套件来检查各种各样的案例,以确保跳跃总是按预期工作。但是,此函数可能不会返回任何值,并具有player.velocity.y = -heightcheckCollisions(player) 的副作用。我想不出一个清晰的单元测试来围绕这个构建。

单元测试在高度有状态的应用程序(如游戏)中不可行吗?单元测试的优势是否如此之大以至于值得对游戏进行功能性编程?


更新:

Games from Within 有一系列关于在游戏中使用测试驱动开发的深入文章。我强烈推荐给任何对这个主题感兴趣的人。这是第一篇文章:

http://gamesfromwithin.com/stepping-through-the-looking-glass-test-driven-game-development-part-1

【问题讨论】:

  • 对于 UnitTest++,请访问 github.com/unittest-cpp/unittest-cpp。其他所有内容都已过时。

标签: unit-testing


【解决方案1】:

通常代码不会将自身分解为不同的单元。

这只是糟糕的设计。代码不会把自己分解成任何东西。作为设计者,你必须在代码上强加一个结构,这样你才能证明它确实有效。

但是,这个函数可能不会返回任何值...

那么?

...还有player.velocity.y = -heightcheckCollisions(player)的副作用。

然后对此进行测试。

我想不出一个清晰的单元测试来围绕这个构建。

为什么不呢?您刚刚对函数的结果给出了很好的说明。

您可能需要一些模拟对象来将成熟的 Player 替换为更易于测试的简化 MockPlayer。

但是你的行为规范是完美的。只需测试您描述的内容即可。

【讨论】:

  • 啊,我明白了。我想我是从字面上解释“单元测试”。我想象测试每个函数的输入和输出。相反,测试可以在调用函数后验证整个系统的约束。谢谢,摆脱我的编程漏斗愿景总是很好:)
  • @Kai:由于系统的状态变化是函数的副作用,所以你还是在做单元测试,因为函数是单独测试的。使用 Mock Objects 进行测试非常容易。我不知道为什么你会声称单元测试在某种程度上仅限于正确功能的输入和输出。
  • 我对这个想法的一个问题是,测试最终可能会像被测试的单元一样复杂且容易出错。以基于滴答的状态变化为例,例如速度,其中没有可设置的值被更改,但可获取的值随着每个滴答而变化。必须创建一个速度行为的仿真来检查,有效地重新实现原始速度代码,但如果测试失败,是单元有问题还是测试有问题?
  • 10 次中有 9 次这样的单元测试在现实世界中根本不实用。大多数不同意的人都是从抽象的角度谈论的。虽然游戏的某些部分可以很容易地进行单元测试,但其中大部分......很容易损坏的复杂部分,创建一组 UT 是不经济的
【解决方案2】:

单元测试对于您可能会在游戏代码中使用的许多低级库很有用,但我严重怀疑它对更高级别的东西有多大用处。游戏通常是模拟,依赖于大量的共享状态,无法有意义地模拟和孤立地进行测试。游戏中的函数通常不会返回任何可以立即检查的值,而是设置一个运行中的过程,该过程应该在将来的某个时间完成。测试这种行为是值得的,但需要一种截然不同的方法来实现独立测试代码片段的单元测试理念。

【讨论】:

  • 这是对我所描述的问题的更清晰的描述,谢谢。启动的过程是否确实按预期结束可能值得一看,但它似乎比测试简单的函数输入和输出更耗时。
【解决方案3】:

编程就是编程。单元测试对任何类型的应用程序都很有用,因为它们可以帮助您立即有效地检测和纠正错误(通常更重要的是,在重构代码时无意中引入的回归)。

当然,有些高级行为领域很难进行单元测试,但这并不是单元测试的真正用途——它们主要用于检查单个方法或代码库的一小部分是否完成了他们应该做的事情.

对于更高级别的行为,您需要应用其他测试方法(回归测试,例如:将固定的输入序列输入到游戏中,然后检查您每次获得相同的结果,或者生成固定的摄像机视图整个关卡中的位置,并检查它们是否始终生成相同(或至少相当相似)的图像)

您的 PlayerJump 示例就是这样一种情况。您可以通过使用恒定输入隔离它来进行单元或回归测试(以编程方式将玩家角色放置在简单测试场景中的固定位置并触发跳跃事件,然后检查他的碰撞或最终休息位置是否一致。通过建立一个玩家可以“跳跃”的不同对象库,您可以涵盖很多测试用例(例如,跳跃成功超过了规定的最大跳跃距离)。

不过,除此之外,游戏确实需要大量的游戏性测试(真正的用户只是简单地玩游戏)。这会发现自动化测试没有涵盖的奇怪情况,但更重要的是它会回答一个自动化测试永远不会回答的问题:它“感觉”对吗?好玩吗”?这些是只有人类才能进行的测试。

【讨论】:

  • +1 请注意,对渲染器进行单元测试几乎是不可能的。有些人使用皮克斯的方法,从相同的角度,使用相同的光照和着色器对相同的控制场景进行快照,然后进行图像比较
  • 编程就是编程,但在交付某些东西、经济、团队和现实世界的背景下,它就不会那样工作了。
  • @JohnStock:确实,必须考虑任何工具/技术的成本/收益。对一切进行单元测试是不切实际的。单元测试没有什么是鲁莽的。技能是找到最好的单元测试折衷方案,瞄准它在效率和成本之间实现最佳平衡的地方。效率也会受到程序员的经验/技能的影响——初级程序员通常会犯更多的错误,因此单元测试会在更大程度上得到帮助。
  • 编程就是编程+1 人们试图通过说“哦,游戏开发者不做单元测试”来证明不进行单元测试,它仍然在编程,并且计算机在检查代码方面仍然比人类更快。那么单元测试对游戏开发有何用处?
【解决方案4】:

我在《孤岛危机 2》开发过程中的单元和自动化测试经验可在此处获得: http://yetanothergameprogrammingblog.blogspot.com/2010/06/aaa-automated-testing.html 希望对您有所帮助。

总结:

自动化测试提高了可交付成果的稳定性,提高了内容创作者和工程师的工作效率 自动化测试是提高代码质量和减少加班机会的有效工具 整个游戏行业总体上是非常反动的,自动化测试遇到了几个反对的非理性论点 不要将其称为测试,将其称为其他东西,几乎其他任何东西(查看行为驱动开发) 保持灵活性,编写好的测试很难,并且需要游戏行业中没有广泛使用的技能

【讨论】:

  • 嗨,欢迎来到 Stack Overflow!始终欢迎提供指向潜在解决方案的链接,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分。想象一下,页面被移动到另一台服务器,或者直接链接发生变化——未来的用户将无法从答案中受益。请看how to answer
  • 链接不起作用,问题是关于单元测试而不是更一般的自动化测试。
【解决方案5】:

许多游戏程序员使用Entity-component-system architecture。他们这样做是因为它可以更容易地修改游戏对象的行为。但事实上,它也使您的代码更容易进行单元测试。

【讨论】:

    【解决方案6】:

    查看PEX 以获取自动化单元测试生成。它将为所有可能的输入变化生成单元测试,这将帮助您测试许多可能的组合。

    【讨论】:

    • 不是只有 PEX .NET 吗?此问题未标记为 .NET 问题。
    • 考虑到 Unity 是游戏开发行业的大玩家,而且它使用 .NET,这个答案仍然很有价值。
    【解决方案7】:

    单元测试根本不关心你的单元有多“有状态”。你有一段或多或少独立的代码,如果它的输入和输出向量很大,那么测试就很困难。无论这些向量是否在执行之前和之后作为状态设置,都不会改变测试。 但是,如果您想告诉我们,您想不出像在大多数头脑简单的单元测试教程/论文中那样定义测试用例的正确方法,即测试主题类似于 "f(x) = y" ,那么是的,我同意,您将很难证明人类手杖提出的少数 (x[100],y[99]) 向量产生足够的覆盖范围(数字!)。尝试制定综合属性和不变量并进行自动化测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-26
      • 1970-01-01
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多