paint-brush
Godot 中的测试:我个人是如何处理它的经过@dlowl
603 讀數
603 讀數

Godot 中的测试:我个人是如何处理它的

经过 D. Lowl4m2024/03/07
Read on Terminal Reader

太長; 讀書

Godot Unit Test (GUT) 可以从 AssetLib 一键安装。它提供了一个我们可以为测试脚本扩展的类。它有一个漂亮的用户界面,能够使用 Godot 的调试器并运行单独的测试。它可以从 CLI 和 CI 中运行(但我稍后会处理这个问题)
featured image - Godot 中的测试:我个人是如何处理它的
D. Lowl HackerNoon profile picture

为了改变节奏,我想做一些开发日志。前段时间,我参加了一个游戏果酱,并制作了这款游戏—— 《老鼠和错误的选择》——一款简短的益智游戏,你在迷宫周围放置奶酪来引诱老鼠。这很有趣,但显然存在一些缺点。


主要问题之一是老鼠的行为不直观。玩家们提到,他们希望老鼠会被不喜欢的奶酪击退,而不仅仅是冻结。另外,实施这种机制将允许更丰富的谜题设计。


所以,我认为这是一个在 Godot 中进行自动化测试的好机会。

预期行为的说明:鼠标应该远离蓝奶酪

测试工具

有一些测试框架可用于 Godot 4,但最吸引我的是Godot Unit Test (GUT) 。 GUT 非常简单:


  • 可以从 AssetLib 一键安装。


  • 它提供了一个我们可以为测试脚本扩展的类:只需添加以test_开头的函数并编写一些断言 - 典型的单元测试结构。


  • 它有一个漂亮的用户界面,能够使用 Godot 的调试器并运行单独的测试。


  • 它可以从 CLI 和 CI 中运行(但我稍后会处理这个问题)。

我的测试框架

对于这种特殊情况,我希望有一种定义复杂场景的方法,就像我定义游戏级别一样 - 在引擎编辑器中而不是在代码中(这样,测试将更接近现实)。因此,我想做这些事情:


  • 有一个运行程序函数,可以接收地图并运行测试。


  • 拥有一组地图,每个地图都有一组要执行的场景(测试用例)。


  • 有一种以拖放方式定义测试用例的方法:放置鼠标,并在 N 轮内设置它应该在的位置。


那么,让我们来揭开这个谜底。

测试用例定义

让我们定义一个新类“MouseTestCase”。我们希望它继承Node2D (因为我们希望将其放置在场景中)。并且我们希望它找到其子级(我们将自己放置在场景上):鼠标和其预期的最终位置(作为标记)

 extends Node2D class_name MouseTestCase @export var steps_left = 0 # How many steps to simulate @export var done = false @onready var mouse: Mouse = $Mouse @onready var expected_position = SnapUtils.get_tile_map_position($TestMarker.position)


现在,我们可以把它放到场景中了,一切都很好!我们知道老鼠从哪里开始,我们知道它应该在哪里结束,以及需要走多少步。

用于定义测试用例的节点树

这就是它在地图上的样子:要测试的鼠标为绿色,目标标记为红色

测试图

现在,让我们制作更多的它们,并制作一张地图来测试我们的驱虫行为。

用于“排斥”机械测试的测试图

这种行为有些复杂,因此,我们想要涵盖许多略有不同的情况:

  • 一只老鼠想离开不喜欢的奶酪/


  • 鼠标想要保持移动方向(即避免转弯)


  • 与右转和掉头相比,老鼠更喜欢左转


上面显示了定义 12 个测试用例来覆盖此行为的结果图(想象一下在代码中对所有这些坐标进行硬编码是多么乏味)。

测试运行者

唯一剩下要做的就是测试运行器功能。该函数需要:

  • 加载我们上面定义的地图。


  • 模拟游戏的前进步骤,直到完成所有测试用例。


  • 在每个步骤中,迭代所有测试用例,如果完成,则检查是否达到预期位置。


代码非常简单。

 func run_level_with_mouse_test_cases(map_path: String): var level = load(map_path) map.load_level(level) var cases = MouseTestCase.cast_all_cases(get_tree().get_nodes_in_group(MouseTestCase.MTC_GROUP_NAME)) while (cases.any(func(case): return not case.done)): map.move_mice() for case in cases: if not case.done: case.steps_left -= 1 if case.steps_left == 0: case.done = true assert_eq(case.get_mouse_position(), case.expected_position, case.get_parent().name+"/"+case.name)

我想这将会发展,但目前的实现已经足够好了。我已经编写了测试,实现了机制,并且测试实际上确认了机制已正确实现!

显示测试运行成功的 GUT 窗格

讨论

在这里,我展示了一种在游戏中进行测试的方法。显然,这里还有很多需要改进的地方,我鼓励读者采用代码和框架并根据自己的需求进行调整。


与往常一样,代码可以在 GitHub 上找到:https: //github.com/d-lowl/of-mice-and-bad-choices您还可以查看介绍测试的具体 PR 。对于奖励积分来说,如果有人能让他们在 CI 中工作,那就太好了。干杯。