Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Unit-Tests

Die Godot Engine ermöglicht das Schreiben von Unit-Tests direkt in C++. Die Engine integriert das doctest-Unit-Testing-Framework, das die Möglichkeit bietet, Test-Suites und Test-Cases neben dem Produktionscode zu schreiben. Da die Tests in Godot jedoch über einen anderen main-Einstiegspunkt laufen, befinden sich die Tests stattdessen in einem eigenen tests/-Verzeichnis, das sich im Stammverzeichnis des Quellcodes der Engine befindet.

Unterstützung von Plattformen und Targets

C++-Unit-Tests können auf Linux-, macOS- und Windows-Betriebssystemen ausgeführt werden.

Tests können nur mit aktivierten Editor-Tools durchgeführt werden, was bedeutet, dass Exportvorlagen derzeit nicht getestet werden können.

Ausführen von Tests

Bevor die Tests tatsächlich ausgeführt werden können, muss die Engine mit der Build-Option tests kompiliert werden (plus jede andere Build-Option, die Sie normalerweise verwenden), da die Tests nicht als Teil der Engine standardmäßig kompiliert werden:

scons tests=yes

Sobald der Build fertig ist, führen Sie die Tests mit der Kommandozeilenoption --test aus:

./bin/<godot_binary> --test

Der Testlauf kann mit den verschiedenen doctest-spezifischen Kommandozeilenoptionen konfiguriert werden. Um die vollständige Liste der unterstützten Optionen zu erhalten, führen Sie den Befehl --test mit der Option --help aus:

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

Alle anderen Optionen und Argumente nach dem Befehl --test werden als Argumente für doctest behandelt.

Bemerkung

Tests werden automatisch kompiliert, wenn Sie die dev_mode=yes SCons Option verwenden. dev_mode=yes wird empfohlen, wenn Sie vorhaben, zur Entwicklung der Engine beizutragen, da Compilerwarnungen automatisch als Fehler behandelt werden. Das Continuous-Integration-System wird fehlschlagen, wenn Compilerwarnungen entdeckt werden, daher sollten Sie sich bemühen, alle Warnungen zu beheben, bevor Sie einen Pull Request erstellen.

Filtern von Tests

Standardmäßig werden alle Tests ausgeführt, wenn Sie keine zusätzlichen Argumente nach dem --test Befehl angeben. Wenn Sie jedoch neue Tests schreiben oder die Ausgabe der erfolgreichen Assertions aus diesen Tests zu Debugging-Zwecken sehen möchten, können Sie die Tests, die Sie interessieren, mit den verschiedenen Filteroptionen von doctest ausführen.

Die Wildcard-Syntax * wird für die Suche nach einer beliebigen Anzahl von Zeichen in Testsuiten, Testfällen und Quellcode-Dateinamen unterstützt:

Filter-Optionen

Abkürzung

Beispiele

--test-suite

-ts

-ts="*[GDScript]*"

--test-case

-tc

-tc="*[String]*"

--source-file

-sf

-sf="*test_color*"

Um zum Beispiel nur die String-Unit-Tests auszuführen, führen Sie aus:

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

Die Ausgabe erfolgreicher Assertions kann mit der Option --success (-s) aktiviert werden und kann z.B. mit jeder Kombination der oben genannten Filteroptionen kombiniert werden:

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

Bestimmte Tests können mit den entsprechenden -exclude Optionen übersprungen werden. Zur Zeit beinhalten einige Tests zufällige Stresstests, die eine gewisse Zeit zur Ausführung benötigen. Um diese Art von Tests zu überspringen, führen Sie den folgenden Befehl aus:

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

Schreiben von Tests

Test-Suites sind C++ Headerdateien, die als Teil des Haupteinstiegspunktes in tests/test_main.cpp eingebunden werden müssen. Die meisten Test-Suites befinden sich direkt im Verzeichnis tests/.

Allen Header-Dateien wird das Präfix test_ vorangestellt. Dies ist eine Namenskonvention, auf die sich das Godot-Buildsystem verlässt, um Tests in der gesamten Engine zu erkennen.

Hier ist eine minimale funktionierende Testsuite mit einem einzigen geschriebenen Test-Case:

#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

Der Header tests/test_macros.h kapselt alles, was zum Schreiben von C++-Unit-Tests in Godot benötigt wird. Er enthält Doctest-Assertion- und Logging-Makros wie CHECK (siehe oben) und natürlich die Definitionen zum Schreiben von Test-Cases selbst.

Siehe auch

tests/test_macros.h Quellcode für derzeit implementierte Makros und Aliase für diese Makros.

Test-Cases werden mit dem funktionsähnlichen Makro TEST_CASE erstellt. Jeder Test-Case muss eine kurze Beschreibung in Klammern haben, optional mit eigenen Tags, die es erlauben, die Tests zur Laufzeit zu filtern, wie [String], [Stress] usw.

Test-Cases werden in einem eigenen Namespace geschrieben. Dies ist nicht erforderlich, ermöglicht es aber, Namenskollisionen zu vermeiden, wenn andere statische Hilfsfunktionen geschrieben werden, um sich wiederholende Testverfahren unterzubringen, wie z.B. das Auffüllen gemeinsamer Testdaten für jeden Test oder das Schreiben parametrisierter Tests.

Godot unterstützt das Schreiben von Tests pro C++-Modul. Anweisungen zum Schreiben von Modultests finden Sie in Schreiben von benutzerdefinierten Unit-Tests.

Assertions

Eine Liste aller häufig verwendeten Assertions, die in den Godot-Tests verwendet werden, sortiert nach Schweregrad.

Assertion

Beschreibung

REQUIRE

Testet, ob die Bedingung erfüllt ist. Der gesamte Test schlägt sofort fehl, wenn die Bedingung nicht zutrifft.

REQUIRE_FALSE

Testet, wenn die Bedingung nicht erfüllt ist. Der gesamten Test schlägt sofort fehl, wenn die Bedingung erfüllt ist.

CHECK

Testet, ob die Bedingung erfüllt ist. Markiert den Testlauf als fehlgeschlagen, erlaubt aber die Ausführung anderer Assertions.

CHECK_FALSE

Testet, ob die Bedingung nicht erfüllt ist. Markiert den Testlauf als fehlgeschlagen, erlaubt aber die Ausführung anderer Assertions.

WARN

Testet, ob die Bedingung erfüllt ist. Lässt den Test unter keinen Umständen fehlschlagen, gibt aber eine Warnung aus, etwas nicht "true" ergibt.

WARN_FALSE

Testet, ob die Bedingung nicht zutrifft. Lässt den Test unter keinen Umständen fehlschlagen, gibt aber eine Warnung aus, wenn etwas "true" ergibt.

Alle oben genannten Assertions haben entsprechende *_MESSAGE-Makros, die es erlauben, eine optionale Nachricht mit der Begründung auszugeben, was erwartet wird.

Bevorzugen Sie CHECK für selbsterklärende Assertions und CHECK_MESSAGE für komplexere, wenn Sie denken, dass diese eine bessere Erklärung verdienen.

Logging

Die Testausgabe wird von doctest selbst gehandhabt und verlässt sich nicht auf die Print- oder Logging-Funktionalität von Godot. Es wird daher empfohlen, spezielle Makros zu verwenden, die es ermöglichen, die Testausgabe in einem von doctest geschriebenen Format zu loggen.

Makro

Beschreibung

MESSAGE

Gibt eine Nachricht aus.

FAIL_CHECK

Markiert den Test als fehlgeschlagen, setzt aber die Ausführung fort. Kann für komplexe Prüfungen in Conditionals verpackt werden.

FAIL

Der Test schlägt sofort fehl. Kann für komplexe Prüfungen in Conditionals verpackt werden.

Zur Laufzeit können verschiedene Reporter gewählt werden. So kann zum Beispiel die Ausgabe in eine XML-Datei umgeleitet werden:

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

Testen von Fehlerpfaden

Manchmal ist es nicht möglich, auf ein erwartetes Ergebnis zu testen. Da die Godot-Entwicklungsphilosophie vorsieht, dass die Engine nicht abstürzen und sich bei Auftreten eines nicht-kritischen Fehlers ordnungsgemäß erholen soll, ist es wichtig zu prüfen, ob diese Fehlerpfade tatsächlich sicher ausgeführt werden können, ohne dass die Engine abstürzt.

Unerwartetes Verhalten kann auf die gleiche Weise getestet werden wie alles andere. Das einzige Problem, das dadurch entsteht, ist, dass der Fehlertext die Testausgabe unnötig mit Fehlern vermüllt, die von der Engine selbst stammen (selbst wenn das Endergebnis erfolgreich ist).

Um dieses Problem zu umgehen, können Sie die Makros ERR_PRINT_OFF und ERR_PRINT_ON direkt in den Testfällen verwenden, um z.B. die Fehlerausgabe der Engine vorübergehend zu deaktivieren:

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.");
}

Test-Tools

Test-Tools sind fortgeschrittene Methoden, die es Ihnen ermöglichen, beliebige Prozeduren auszuführen, um den Prozess des manuellen Testens und des Debuggens der Engine-Interna zu erleichtern.

Diese Tools können durch die Angabe des Namens eines Tools nach der --test Kommandozeilenoption gestartet werden. Zum Beispiel implementiert und registriert das GDScript-Modul mehrere Tools, die bei der Fehlersuche im Tokenizer, Parser und Compiler helfen:

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

Wird ein solches Tool entdeckt, werden die restlichen Unit-Tests übersprungen.

Test-Tools können überall in der Engine registriert werden, da der Registrierungsmechanismus dem von doctest ähnelt, wenn Testfälle mit dynamischer Initialisierungstechnik registriert werden, aber normalerweise können diese in den entsprechenden register_types.cpp-Quellen (pro Modul oder Kern) registriert werden.

Hier ist ein Beispiel dafür, wie GDScript Test-Tools in modules/gdscript/register_types.cpp registriert:

#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

Das benutzerdefinierte Parsen der Kommandozeile kann von einem Test-Tool selbst mit Hilfe der Methode OS get_cmdline_args durchgeführt werden.

Integrationstests für GDScript

Godot verwendet doctest, um Regressions-Bugs in GDScript während der Entwicklung zu verhindern. Es gibt mehrere Arten von Testskripten, die geschrieben werden können:

  • Tests für erwartete Fehler;

  • Tests für Warnungen;

  • Tests für Performancemerkmale.

Der Prozess der Erstellung von Integrationstests für GDScript sieht daher wie folgt aus:

  1. Wählen Sie einen Typ eines Testskripts, das Sie schreiben möchten, und erstellen Sie eine neue GDScript-Datei im Verzeichnis modules/gdscript/tests/scripts im entsprechenden Unterverzeichnis.

  2. Schreiben Sie GDScript-Code. Das Testskript muss eine Funktion namens test() enthalten, die keine Argumente benötigt. Diese Funktion wird vom Test-Runner aufgerufen. Der Test sollte keine Abhängigkeiten haben, es sei denn, sie sind auch Teil des Tests. Globale Klassen (mit class_name) werden vor dem Start des Test-Runners registriert, so dass diese bei Bedarf funktionieren sollten.

    Hier ist ein Beispiel für ein Testskript:

    func test():
        if true # Missing colon here.
            print("true")
    
  3. Wechseln Sie in das Stammverzeichnis des Godot-Quellcode-Repositorys.

    cd godot
    
  4. Erzeugen Sie *.out-Dateien, um die erwarteten Ergebnisse der Ausgabe zu aktualisieren:

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

Sie können die Option --print-filenames hinzufügen, um die Dateinamen zu sehen, während die Testausgaben erzeugt werden. Wenn Sie an einem neuen Feature arbeiten, die schwere Abstürze verursacht, können Sie diese Option verwenden, um schnell herauszufinden, welche Testdatei den Absturz verursacht, und von dort aus debuggen.

  1. Führen Sie GDScript-Tests aus mit:

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

Dies akzeptiert auch die Option --print-filenames (siehe oben).

Wenn keine Fehler ausgegeben werden und alles gut geht, sind Sie fertig!

Warnung

Vergewissern Sie sich, dass die Ausgabe die erwarteten Werte hat, bevor Sie einen Pull Request einreichen. Wenn --gdscript-generate-tests *.out-Dateien erzeugt, die nichts mit den neu hinzugefügten Tests zu tun haben, sollten Sie diese Dateien reverten und nur *.out Dateien für neue Tests committen.

Bemerkung

Der GDScript-Testrunner ist zum Testen der GDScript-Implementierung gedacht, nicht zum Testen von Benutzerskripten oder zum Testen der Engine mithilfe von Skripten. Wir empfehlen, neue Tests für bereits gelöste Issues im Zusammenhang mit GDScript auf GitHub zu schreiben, oder Tests für derzeit in Arbeit befindliche Features zu schreiben.

Bemerkung

Wenn Ihr Test Case erfordert, dass innerhalb der Skriptdatei keine test() Funktion vorhanden ist, können Sie den Laufzeitabschnitt des Tests deaktivieren, indem Sie die Skriptdatei so benennen, dass sie dem Muster *.notest.gd entspricht. Zum Beispiel: "test_empty_file.notest.gd".