Alt-F4 #48 - Angel 的自动化单元测试  2021-09-24

作者 Lovely_Santa, 编辑 stringweasel, Nanogamer7, Conor_, Therenas, Firerazer,
翻译 Ph.X

目录

时隔很久,一位名叫 Lovely_Santa 的远古撰稿人又回到了他的笔下!上一次听到这位主人公的消息要追溯到第 2 期,当时他正在试验(彼时)最新的蜘蛛机甲的 Mod 能力。而这一次,他真的继续了 Angel Mod 的主题,同时写了一些完全不同的东西……

自动化 Mod 测试的一次成功尝试 Lovely_Santa

在给你讲一个如何自动化创建异星工厂 Mod 中无聊部分的故事之前,我必须先回到起点……很久很久以前——不对,我得从 2019 年年底的某个时候开始。我指的不是众所周知的 COVID-19 开始传播的故事,而是 Arch666Angel 退出他那知名的 Angel’s mod 工作的悲伤故事。这是由于他现实生活中变化造成的。我不想费力解释所有的细节,但这导致了一些 Mod 作者产生了接管他的项目和遗产的兴趣。

直到 2020 年 1 月 27 日 Arch666Angel 才同意由社区为他的作品作贡献。在商定了所有细则后,他选择了几个人来延续他的 Mod 的愿景,然后我们才向公众通报此事。当时的主要互动是在异星工厂论坛进行的,你仍然可以在那里看到公告信息

在 2020 年 1 月,我们开始迫不及待地将 Mod 从 0.17 更新到 0.18,包括基础游戏所引入的所有突破性变化。经 Arch666Angel 批准,我们发布了第一个 “初始 0.18 版本 “!正在我们庆祝发布之时,现实给了我们一记耳光,论坛上出现了大量的错误报告。此时我们尚不清楚后续工作会有多大,要花掉多少时间来修复错误,游戏社区在帮助解决这些问题中会有多么伟大。

始于足下

在社区的帮助下,我们了解了 Angel 库的工作原理。对于那些不知道什么是库的人来说,它是一组辅助函数,对异星工厂代码库进行抽象,使其更容易编程。它主要是一系列开发者所系的便利功能,让他们可以专注于开发功能,而不是花时间重复写样板代码。你可以将 Angel 的库与一些独立的 Mod 进行比较,比如异星工厂库Bob 的函数库 Mod,甚至连我自己也尝试过:Lovely_santa 的知识库。实现一个库没有“正确”或“错误”的方法,只要符合其目的即可。通过了解 Angel 的库,它实际上位于 Angel 精炼中,而非一个独立的 Mod,我们找到了更有效的方法来解决 Mod 中出现的某些 Bug。

我有信心说,我们不会再犯导致 Mod 硬崩溃的代码错误。现在的问题可能是边界情况,未处理的情况或奇怪的 Mod 组配置。我们最常收到的 Bug 来自于一些玩 Angel 加 Bob 的玩家。原因很简单:有太多种配置和设置,导致了很多未预见的情况。一个小例子:你修复了一个在如下奇特配置下导致某技术无法研究的错误。它无法研究的原因很简单。它有一个尚未研究的先决技术,但由于先决条件是隐藏的,所以它在技术树中没有显示出来。

镍加工无法研究

过段时间你注意到另一个配置有类似的问题。

镍加工又双叒叕无法研究

我们开始创建一组必须测试的配置列表来作为一个解决方案。pezzawinkle 最近制定了一个不太小的列表,其中包括他每次准备发布时所测试的全部配置。

就个人而言,我会用所有受影响的配置来测试我做的每一个改动。修复 Bug 最乏味的部分是不得不一次又一次地将所有 Angel Mod 重新加载到 Mod 文件夹中。2020 年 4 月 11 日,我创建了一个 python 小脚本来更新所有 Angel Mod 到最新。

这个脚本为我节省了很多时间,让我在修改完仍未达成预期效果的那一行代码时,可以抽空喝杯咖啡休息的同时重新加载 Mod。通过长期使用这个脚本,我又遇到了一个问题:Bob。是的,Bobbingabout 发布了一个他的 Mod 更新,破坏了我刚刚修复 Bug 的所有工作……2021 年 5 月 3 日,差不多一年后,我创建了另一个 python 小脚本,从 Mod 门户网站上下载所有的 Bob Mod,而不是在游戏中一个一个的下。

滚雪球

在2020年底,我们决定在 Angel 工业里面做一个大功能:像样的实现组件和技术的大修。这是 Arch666Angel 在退出 Mod 制作之前做最后一个功能。2021 年 5 月 26 日(上次发布时隔半年之后),我们向公众展示了新版本的大修,之后错误报告开始快速涌现。

我们意识到,随着大修的进行,我们创造了更多的配置和设置。此时,完全靠手工维护和测试变得相当困难。我开始考虑创建一些单元测试来减轻我们肩上的负担。经过一些讨论和来自急公好义的 Mod 社区的主意,我终于在 2021 年 6 月 30 日决定致力于创建单元测试基础设施,并将其归入一个单独的 Mod:Angel 开发 - 单元测试(Angel’s Dev - Unit Tests)。不过你在 Mod 门户上可找不到这个 Mod。

我已经有了一个将 Angel Mod 从 Github 文件夹构建到异星工厂 Mod 文件夹的方法,还有一个类似的脚本从 Mod 门户下载 Bob Mod。为了测试不同的配置,我不得不另外写两个脚本。第一个脚本读取并重写带有 Mod 设置的 mod-settings.dat 文件。设置文件存储了所有用户配置的设置,这可能导致不同的 Mod 配置。例如,在 Angel 工业中,它可以用来在特殊原版Bob-Angel(=常规大修,没有Bob Mod 也能用)、组件大修科学大修之间切换。基本上来讲这里可以通过几个复选框来改变 Angel Mod 的全部行为。

Angel 工业里的设置

由于 Mod 设置的加载和保存部分完全由游戏本体来处理,所以这是我从前制作 Mod 时未曾遇到的部分。不过,我很惊讶地发现异星工厂 Wiki 上的文档有详尽地讲解这个问题。

第二个脚本配置 mod-list.json 文件,其中包含要启用的 Mod 的信息。这是人们最熟悉的玩有 Mod 的异星工厂部分。他们从门户网站下载 Mod,在完成游戏后,禁用 Mod 并尝试一些其他 Mod。下面是一个 Bob Angel 的 Mod 列表的例子,完全通过脚本配置。

启用了所有 Bob Angel Mod 的异星工厂 Mod 列表,通过脚本进行配置

有了这四个脚本,一切都准备就绪,可以用最新的 Mod 配置及其某种 Mod 设置来启动异星工厂。剩下的只是一个告诉异星工厂以不同的 Mod 设置反复启动的问题。假设游戏中的 Mod(本例中为’Angel 开发 - 单元测试’)告诉测试基础设施测试已经完成,它可以关闭异星工厂并启动下一个配置来测试。此时,单元测试基础设施可以检查是否所有的配置都成功加载而不崩溃。在加载游戏后,单元测试模块可以在运行时验证游戏过程。

最后一片拼图

一周后,2021 年 6 月 8 日,我完成了单元测试基础的编写,该测试除了报告哪些 Mod 在游戏中启用外,没有其他作用。在单元测试完成记录结果后,主脚本可以启动下一个配置。下面是一个单一配置的单一单元测试的输出样本:

angelsdev-unit-test: Testing Special vanilla (light)
angelsdev-unit-test: Launching factorio.exe
angelsdev-unit-test: Starting 1 unit tests...
angelsdev-unit-test: Starting unit test 001.
angelsdev-unit-test:     Unit testing mod configuration:
angelsdev-unit-test:     angelsdev-unit-test version 0.0.1
angelsdev-unit-test:     angelspetrochem version 0.9.21
angelsdev-unit-test:     angelsrefining version 0.12.1
angelsdev-unit-test:     angelssmelting version 0.6.18
angelsdev-unit-test:     base version 1.1.38
angelsdev-unit-test: Unit test 001 PASSED!
angelsdev-unit-test: Finished testing! All unit tests passed!
angelsdev-unit-test: Closing factorio.exe
... <output of other configurations>
angelsdev-unit-test: Summary:
angelsdev-unit-test: [PASSED] Special vanilla (light)
angelsdev-unit-test: [PASSED] Special vanilla (regular)
angelsdev-unit-test: [PASSED] Special vanilla (extended)
angelsdev-unit-test: [PASSED] Special vanilla (BA)
angelsdev-unit-test: [PASSED] BA (light)
angelsdev-unit-test: [PASSED] BA (regular)
angelsdev-unit-test: [PASSED] BA (extended)
angelsdev-unit-test: [PASSED] BA (extended components)
angelsdev-unit-test: [PASSED] BA (extended technology)
angelsdev-unit-test: [PASSED] BA (BobPower non-default + overhaul)
angelsdev-unit-test: [PASSED] BA (BobPower non-default + components)
angelsdev-unit-test: [PASSED] BA (BobPower non-default + technology)
angelsdev-unit-test: [PASSED] BA (BobAssembly non-default + overhaul)
angelsdev-unit-test: [PASSED] BA (BobLogistics non-default + overhaul)
angelsdev-unit-test: [PASSED] BA (BobRevamp non-default + overhaul)
angelsdev-unit-test: [PASSED] BA (Bob other non-default + overhaul)
angelsdev-unit-test: [PASSED] Pure Angels (overhaul)
angelsdev-unit-test: [PASSED] Pure Angels (components)
angelsdev-unit-test: [PASSED] Pure Angels (technology)

任何开发者都知道,这个最简可行产品(Minimum Viable Product,MVP)仍然可以继续打磨。经过一些广泛的使用,在 2021 年 8 月 22 日,我又增加了两个功能,其中第一增加了一个包含所有测试配置的独立文件,这样就可以更容易地根据需要添加新的配置。第二增加了将测试结果记录到一个文件的功能。这样做,新的测试结果可以通过使用一些外部工具,如 ExamDiff Pro 来与旧的测试结果进行比较。这主要是为了验证一些已经解决的问题(在新的测试结果中缺少的行),而没有引入新的问题(在新的测试结果中增加的行)。

结论

自从使用该最小可行产品以来,我们已经发布了一个更新。经过两个星期的等待,我们惊讶地发现,我们几乎没有任何破坏性的 Bug。在维护 Angel 的 Mod 时,我个人有一个新“规则”;如果发现了一个 Bug,在解决这个问题之前,先创建一个恰当的单元测试来捕捉这个 Bug。不幸的是,运行所有的单元测试需要相当长的时间,但这很容易通过在喝咖啡休息时运行单元测试来解决。

目前,我们已有的(少数)单元测试只检查游戏的加载阶段(也称为数据阶段)。然而,没有什么限制我们创建复杂的单元测试来测试游戏中的行为(也称为运行阶段)。

我希望这能帮助我(作为一个开发者)在未来花更少的时间来修复错误,而花更多的时间来实现新的功能。我知道这对于小型的独立 Mod 来说并不必要,但是对于维护相当大的代码库来说是非常有用的,特别是当你和多人一起开发的时候,你不可能去看别人写的每一行代码,也不可能知道每个功能的所有细节。

这是我个人的故事,解释了我是如何亲身体会到大公司是如何以及为什么使用单元测试、代码集成和许多自动化工具来自动化推送新功能的过程,相信代码会成功地完成它的目的,而不是用一段没有说明的信息“任务成功失败。请与开发人员联络。”来让数百万人感到沮丧。

征稿

一如既往的,我们正在召集任何想要为 Alt-F4 做出贡献的人,无论是提交文章还是帮助翻译都可以。如果您有些有趣的想法,并乐于与社区分享,这里就是一个好地方。如果您没有太大把握,我们会很乐意帮助您讨论内容创意和结构问题。如果您有意参与,从加入 Discord 开始吧!