Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

位元移位

Godot Engine 允許直接用 C++ 寫單元測試。該引擎整合了「doctest <https://github.com/onqtam/doctest>」_單元測試框架,該框架提供了在生產程式碼旁邊編寫測試套件和測試案例的能力,但由於Godot 中的測試經歷了不同的「 main 入口點,測試駐留在專用的 tests/ 目錄中,該目錄位於引擎原始碼的根目錄下。

支援平台

C++ 單元測試可以在 Linux、macOS 和 Windows 作業系統上運作。

測試只能在啟用編輯器「tools」的情況下運作,這表示目前無法測試匯出範本。

執行遊戲

在實際執行測試之前,必須在啟用「tests」建置選項(以及您通常使用的任何其他建置選項)的情況下編譯引擎,因為預設情況下測試不會編譯為引擎的一部分:

scons tests=yes

建置完成後,使用“--test”命令列選項執行測試:

./bin/<godot_binary> --test

可以使用各種特定於 doctest 的命令列選項來設定測試運作。若要檢索支援選項的完整列表,請執行帶有“--help”選項的“--test”命令:

./bin/<godot_binary> --test --help

“--test” 指令之後的任何其他選項和參數都被視為 doctest 的參數。

備註

如果您使用“dev_mode=yes” SCons 選項,測試會自動編譯。如果您打算為引擎開發做出貢獻,建議使用“dev_mode=yes”,因為它會自動將編譯警告視為錯誤。如果偵測到任何編譯警告,持續整合系統將會失敗,因此您應該在開啟拉取請求之前努力修復所有警告。

篩選檔案

預設情況下,如果您在“--test”命令後不提供任何額外參數,則會執行所有測試。但是,如果您正在編寫新測試或希望查看這些測試的成功判斷提示輸出以進行偵錯,則可以使用 doctest 提供的各種篩選選項來執行感興趣的測試。

支援通配符語法「*」來配對測試套件、測試案例和原始檔案名稱中的任意數量的字元:

通用選項

Shorthand

範例

--test <test>

-ts

-ts="*[GDScript]*"

--no-docbase

-tc

String[]

--quiet

-sf

-sf="*test_color*"

例如,若要僅執行“String”單元測試,請執行:

./bin/<godot_binary> --test --test-case="*[String]*"

成功的判斷提示輸出可以使用“--success”(“-s”)選項啟用,並且可以與上述篩選選項的任意組合結合使用,例如:

./bin/<godot_binary> --test --source-file="*test_color*" --success

可以使用對應的“-exclude”選項跳過特定測試。截至目前,一些測試包括隨機壓力測試,需要一段時間才能執行。若要跳過此類測試,請執行以下命令:

./bin/<godot_binary> --test --test-case-exclude="*[Stress]*"

撰文風格

測試套件代表 C++ 頭檔案,必須將其作為主測試入口點的一部分包含在「tests/test_main.cpp」中。大多數測試套件直接位於“tests/”目錄下。

所有頭檔都以「test_」為前綴,這是 Godot 建置系統用來偵測整個引擎測試的命名約定。

這是一個最小的工作測試套件,其中編寫了一個測試案例:

#ifndef TEST_STRING_H
#define TEST_STRING_H

#include "tests/test_macros.h"

namespace TestString {

TEST_CASE("[String] Hello World!") {
    String hello = "Hello World!";
    CHECK(hello == "Hello World!");
}

} // namespace TestString

#endif // TEST_STRING_H

備註

You can quickly generate new tests using the create_test.py script found in the tests/ directory. This script automatically creates a new test file with the required boilerplate code in the appropriate location. It's also able to automatically include the new header in tests/test_main.cpp using invasive mode (-i flag). To view usage instructions, run the script with the -h flag.

“tests/test_macros.h” 標頭封裝了所有在 Godot 中編寫 C++ 單元測試所需的內容。它包括 doctest 判斷提示和紀錄記錄宏,例如上面看到的“CHECK”,當然還有編寫測試案例本身的定義。

也參考

有關各個錯誤巨集的更多資訊,請參考 Godot 原始碼中的 core/error_macros.h

Test cases are created using TEST_CASE function-like macro. Each test case must have a brief description written in parentheses, optionally including custom tags which allow to filter the tests at runtime, such as [String], [Stress] etc.

測試案例編寫在專用的命名空間中。這不是必需的,但可以防止在編寫其他靜態幫助器函式以適應重複測試過程(例如為每個測試填充公共測試資料或編寫參數化測試)時發生命名衝突。

有關如何建立模組圖示,請參考 Creating custom module icons

Subcases

In situations where you have a common setup for several test cases with only slight variations, subcases can be very helpful. Here's an example:

TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
    // ... common setup (e.g. creating a scene tree with a few nodes)
    SUBCASE("Move node to specific index") {
        // ... setup and checks for moving a node
    }
    SUBCASE("Remove node at specific index") {
        // ... setup and checks for removing a node
    }
}

Each SUBCASE causes the TEST_CASE to be executed from the beginning. Subcases can be nested to an arbitrary depth, but it is advised to limit nesting to no more than one level deep.

注意事項

Godot 測試中常用的判斷提示列表,按嚴格程度排序。

版本

描述

REQUIRE

檢查條件是否成立。如果條件不成立則會立即讓整個測試失敗。

REQUIRE_FALSE

檢查條件是否不成立。如果條件成立則會立即讓整個測試失敗。

CHECK

檢查條件是否成立。會將測試表示為失敗,但允許運作其他判斷提示。

CHECK_FALSE

檢查條件是否不成立。會將測試表示為失敗,但允許運作其他判斷提示。

WARN

檢查條件是否成立。任何情況下都不會讓測試失敗,但是不成立時會記錄一條警告。

WARN_FALSE

檢查條件是否不成立。任何情況下都不會讓測試失敗,但是成立時會記錄一條警告。

以上判斷提示都有對應的 *_MESSAGE 宏,能夠在原有行為的基礎上輸出可選的消息。

對於能夠自我說明的判斷提示請儘量使用 CHECK,如果你認為相對複雜的判斷提示需要更好的解釋再使用 CHECK_MESSAGE

偵錯

測試輸出由 doctest 本身處理,根本不依賴 Godot 列印或紀錄記錄功能,因此建議使用專用宏,它允許以 doctest 編寫的格式記錄測試輸出。

Macro

描述

MESSAGE

列印一條訊息。

FAIL_CHECK

將測試標記為失敗,但繼續執行。可以包含在條件敘述中以進行複雜的檢查。

FAIL

測試立即失敗。可以包含在條件敘述中以進行複雜的檢查。

Different reporters can be chosen at runtime. For instance, here's how the output can be redirected to an XML file:

./bin/<godot_binary> --test --source-file="*test_validate*" --success --reporters=xml --out=doctest.txt

匯入翻譯

有時,測試「預期」結果並不總是可行。按照 Godot開發理念,引擎不應當機,並且在發生非致命錯誤時應正常恢復,因此檢查這些故障路徑是否確實可以安全執行而不會使引擎當機非常重要。

*意外*行為可以用與其他任何行為相同的方式進行測試。這產生的唯一問題是錯誤列印會不必要地污染來自引擎本身的錯誤的測試輸出(即使最終結果是成功的)。

為了緩解這個問題,請直接在測試案例中使用“ERR_PRINT_OFF”和“ERR_PRINT_ON”巨集來暫時停用來自引擎的錯誤輸出,例如:

TEST_CASE("[Color] Constructor methods") {
    ERR_PRINT_OFF;
    Color html_invalid = Color::html("invalid");
    ERR_PRINT_ON; // Don't forget to re-enable!

    CHECK_MESSAGE(html_invalid.is_equal_approx(Color()),
        "Invalid HTML notation should result in a Color with the default values.");
}

Special tags in test case names

These tags can be added to the test case name to modify or extend the test environment:

Tag

描述

[SceneTree]

Required for test cases that rely on a scene tree with MessageQueue to be available. It also enables a mock rendering server and ThemeDB.

[Editor]

Like [SceneTree], but with additional editor-related infrastructure available, such as EditorSettings.

[Audio]

Initializes the AudioServer using a mock audio driver.

[Navigation]

Creates the default 2D/3D navigation servers and makes them available for testing.

You can use them together to combine multiple test environment extensions.

Testing signals

The following macros can be use to test signals:

Macro

說明

SIGNAL_WATCH(object, "signal_name")

Starts watching the specified signal on the given object.

SIGNAL_UNWATCH(object, "signal_name")

Stops watching the specified signal on the given object.

SIGNAL_CHECK("signal_name", Vector<Vector<Variant>>)

Checks the arguments of all fired signals. The outer vector contains each fired signal, while the inner vector contains the list of arguments for that signal. The order of signals is significant.

SIGNAL_CHECK_FALSE("signal_name")

Checks if the specified signal was not fired.

SIGNAL_DISCARD("signal_name")

Discards all records of the specified signal.

Below is an example demonstrating the use of these macros:

//...
SUBCASE("[Timer] Timer process timeout signal must be emitted") {
    SIGNAL_WATCH(test_timer, SNAME("timeout"));
    test_timer->start(0.1);

    SceneTree::get_singleton()->process(0.2);

    Array signal_args;
    signal_args.push_back(Array());

    SIGNAL_CHECK(SNAME("timeout"), signal_args);

    SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
}
//...

2D 工具

測試工具是先進的方法,可讓您運作任意程式,以方便手動測試和除錯引擎內部的過程。

可以透過在「--test」命令列選項後提供工具名稱來運作這些工具。例如,GDScript 模組實作並註冊了幾個工具來幫助偵錯分詞器、解析器和編譯器:

./bin/<godot_binary> --test gdscript-tokenizer test.gd
./bin/<godot_binary> --test gdscript-parser test.gd
./bin/<godot_binary> --test gdscript-compiler test.gd

如果偵測到任何此類工具,則跳過其餘的單元測試。

測試工具可以在整個引擎的任何地方註冊,因為註冊機制與doctest 在使用動態初始化技術註冊測試案例時提供的機制非常相似,但通常這些可以在相應的「register_types.cpp」來源中註冊(每個模組或核心) 。

以下是 GDScript 如何在「modules/gdscript/register_types.cpp」中註冊測試工具的範例:

#ifdef TESTS_ENABLED
void test_tokenizer() {
    TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
}

void test_parser() {
    TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
}

void test_compiler() {
    TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
}

REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
REGISTER_TEST_COMMAND("gdscript-parser", &test_parser);
REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler);
#endif

自訂命令列解析可以由測試工具本身在 OS get_cmdline_args<class_OS_method_get_cmdline_args>` 方法的幫助下執行。

在 GDScript 中實體化 C# 節點

Godot 使用 doctest 來防止開發過程中 GDScript 中的迴歸。可以編寫多種型別的測試腳本:

  • 測試預期錯誤;

  • 警告測試;

  • 功能列表

因此,為GDScript編寫整合測試的流程如下:

  1. 選擇您想要編寫的測試腳本型別,然後在對應子目錄中的「modules/gdscript/tests/scripts」目錄下建立新的 GDScript 檔案。

  2. 編寫 GDScript 程式碼。測試腳本必須有一個名為「test()」的函式,該函式不帶參數。這樣的函式將由測試運作器呼叫。測試不應該有任何依賴性,除非它也是測試的一部分。全域類別(使用“class_name”)在運作器啟動之前註冊,因此如果需要的話這些應該可以工作。

    下列為有兩個骨頭的骨骼節點範例:

    func test():
        if true # Missing colon here.
            print("true")
    
  3. Change directory to the Godot source repository root.

    cd godot
    
  4. 產生“*.out”檔案以更新輸出的預期結果:

    bin/<godot_binary> --gdscript-generate-tests modules/gdscript/tests/scripts
    

您可以新增「--print-filenames」選項來查看產生測試輸出的檔案名稱。如果您正在開發導致硬當機的新功能,則可以使用此選項快速尋找導致當機的測試檔案並從那裡進行偵錯。

  1. GDScript的匯出

    ./bin/<godot_binary> --test --test-suite="*GDScript*"
    

這也接受“--print-filenames”選項(見上文)。

如果沒有列印任何錯誤並且一切順利,那麼您就完成了!

警告

在提交拉取請求之前,請確保輸出確實具有預期值。如果「--gdscript-generate-tests」產生與新新增的測試無關的「.out」檔案,您應該恢復這些檔案並只為新測試提交「.out」檔案。

備註

GDScript 測試執行程式旨在測試 GDScript 實作,而不是測試使用者腳本或使用腳本測試引擎。我們建議為GitHub 上已解決的「與GDScript 相關的問題<https://github.com/godotengine/godot/issues?q=is%3Aissue+label%3Atopic%3Agdscript+is%3Aheld>」編寫新測試,或為目前工作的功能編寫測試。

備註

如果您的測試案例要求腳本檔案中不存在「test()」函式,您可以透過命名腳本檔案來停用測試的執行時間部分,使其與模式「*.notest.gd」相符``。例如,「test_empty_file.notes.gd」。