Bei der Entwicklung von Spielen können manuelle Tests schnell repetitiv und fehleranfällig werden. Haben Sie sich schon einmal in einem dieser scheinbar endlosen Testzyklen wiedergefunden, während Sie an einer neuen Funktion arbeiten oder versuchen, einen Fehler zu beheben?
Durch die Automatisierung Ihrer Code-Tests können Sie mehr Zeit für die kreative Entwicklung von Spielen aufwenden und weniger für sich wiederholende (aber wichtige) QA-Aufgaben, die sicherstellen, dass das Hinzufügen, Entfernen oder Ändern von Code Ihr Projekt nicht beschädigt.
Unity unterstützt Sie bei der Erstellung, Verwaltung und Ausführung automatisierter Tests für Ihre Spiele mit dem Unity Test Framework.
Mit dem Unity Test Framework (UTF) können Sie Ihren Projektcode sowohl im Edit- als auch im Play-Modus testen. Sie können den Testcode auch für verschiedene Plattformen wie Standalone, iOS oder Android ausrichten.
Der UTF wird installiert, indem er mit dem Paketmanager zu Ihrem Projekt hinzugefügt wird.
Unter der Haube ist UTF mit NUnit integriert, einer bekannten Open-Source-Testbibliothek für .NET-Sprachen.
Es gibt zwei Hauptkategorien von Tests, die Sie mit UTF schreiben können: den Bearbeitungsmodus und den Abspielmodus:
Die Tests im Bearbeitungsmodus laufen im Unity-Editor und haben Zugriff auf den Editor und den Spielcode. Dies bedeutet, dass Sie Ihre benutzerdefinierten Editor-Erweiterungen testen oder Tests verwenden können, um Einstellungen im Editor zu ändern und den Abspielmodus aufzurufen, der nützlich ist, um Inspektorwerte anzupassen und dann automatisierte Tests mit vielen verschiedenen Einstellungen durchzuführen.
Mit den Tests im Spielmodus können Sie Ihren Spielcode zur Laufzeit testen. Tests werden in der Regel als Coroutine unter Verwendung des Attributs [UnityTest] ausgeführt. So können Sie Code testen, der über mehrere Frames hinweg ausgeführt werden kann. Standardmäßig werden die Tests im Wiedergabemodus im Editor ausgeführt, aber Sie können sie auch in einem eigenständigen Player-Build für verschiedene Zielplattformen ausführen.
Um diesem Beispiel folgen zu können, müssen Sie das Paket Starter Assets - Third Person Character Controller aus dem Unity Asset Store installieren und in ein neues Projekt importieren.
Installieren Sie UTF über Fenster > Paketmanager. Suchen Sie im Paketmanager unter der Unity-Registry nach Test Framework. Stellen Sie sicher, dass Sie die Version 1.3.3 (die zum Zeitpunkt der Erstellung dieses Dokuments aktuellste Version) auswählen.
Sobald UTF installiert ist, öffnen Sie die Datei Packages/manifest.json mit einem Texteditor und fügen Sie einen Abschnitt testables nach dependencies ein, etwa so:
,
"testables": [
"com.unity.inputsystem"
]
Speichern Sie die Datei. Dies wird später nützlich sein, wenn Sie die Unity.InputSystem.TestFramework-Assembly zum Testen und Emulieren von Spielereingaben referenzieren müssen.
Kehren Sie zum Editor zurück und lassen Sie die neuere Version installieren.
Klicken Sie auf Fenster > Allgemein > Test Runner, um das Fenster des Test Runner-Editors anzuzeigen.
In diesem Teil des Tutorials liegt der Schwerpunkt auf der Erstellung von Tests im Spielmodus. Anstatt die Optionen zum Erstellen von Testgruppenordnern im Test Runner-Fenster zu verwenden, erstellen Sie sie im Projektfenster.
Klicken Sie mit der rechten Maustaste auf das Stammverzeichnis Ihres Projekt-Asset-Ordners, und wählen Sie Erstellen > Testen > Tests Assembly-Ordner.
Es wird ein Projektordner Tests hinzugefügt, der eine Datei Tests.asmdef (Assembly Definition) enthält. Dies ist erforderlich, damit die Tests auf Ihre Spielmodule und Abhängigkeiten verweisen können.
Der Zeichensteuerungscode wird in Tests referenziert und benötigt ebenfalls eine Assembly-Definition. Als Nächstes werden Sie einige Baugruppendefinitionen und Referenzen einrichten, um das Testen zwischen den Modulen zu erleichtern.
Klicken Sie mit der rechten Maustaste auf den Projektordner Assets/StarterAssets/InputSystem, und wählen Sie Erstellen > Baugruppendefinition. Geben Sie ihm einen aussagekräftigen Namen, zum Beispiel StarterAssetsInputSystem.
Wählen Sie die neue Datei StarterAssetsInputSystem.asmdef aus und fügen Sie mithilfe des Inspektors eine Assembly-Definitionsreferenz zu Unity.InputSystem hinzu. Klicken Sie auf Anwenden.
Klicken Sie mit der rechten Maustaste auf den Projektordner Assets/StarterAssets/ThirdPersonController/Scripts, und wählen Sie Erstellen > Assembly-Definition. Geben Sie ihm einen aussagekräftigen Namen, zum Beispiel ThirdPersonControllerMain.
Wie bei der vorherigen Assembly-Definition öffnen Sie ThirdPersonControllerMain im Inspector und wählen Referenzen für aus:
- Unity.InputSystem
- StarterAssetsInputSystem
Klicken Sie auf Anwenden.
Um Teile des Eingabesystems zu emulieren, müssen Sie es in Ihren Tests referenzieren. Außerdem müssen Sie den StarterAssets-Namespace in einer Assembly referenzieren, die Sie für den Code des Third Person Controllers erstellen werden.
Öffnen Sie Tests.asmdef im Inspector und fügen Sie einen Verweis auf die folgenden Assembly-Definitionen hinzu:
- UnityEngine.TestRunner
- UnityEditor.TestRunner
- Unity.InputSystem
- Unity.InputSystem.TestFramework
- ThirdPersonControllerMain
Klicken Sie auf Anwenden.
Ihr erster Test wird einige Grundlagen zum Laden und Bewegen der Hauptfigur aus dem Third-Person-Controller-Paket abdecken.
Beginnen Sie mit dem Einrichten des neuen Projekts mit einer einfachen Testumgebung und einer Prefab-Ressource für den Charakter, mit der Sie arbeiten können.
Öffnen Sie die Szene mit dem Namen Assets/StarterAssets/ThirdPersonController/Scenes/Playground.unity und speichern Sie eine Kopie davon über das Menü Datei > Speichern unter unter diesem neuen Pfad: Assets/Scenes/SimpleTesting.unity
Wenn Sie rosafarbene Materialien in der Spielansicht sehen, verwenden Sie den Render-Pipeline-Konverter, um Materialien von der integrierten Render-Pipeline auf die universelle Render-Pipeline (URP) zu aktualisieren. In diesem Artikel finden Sie einen kurzen Überblick.
Erstellen Sie in Ihrem Projekt-Assets-Ordner einen neuen Ordner namens Ressourcen. Anmerkung: Der Ordnername "Resources" ist hier wichtig, damit die Methode Unity Resources.Load() verwendet werden kann.
Ziehen Sie das PlayerArmature GameObject in der Scene-Ansicht in den neuen Resources-Ordner und wählen Sie bei der Aufforderung, eine Original Prefab zu erstellen. Benennen Sie das vorgefertigte Asset Charakter um.
Dies wird die Basiszeichenvorlage sein, die in Ihren zukünftigen Tests verwendet wird.
Entfernen Sie das PlayerArmature GameObject aus der neuen SimpleTesting-Szene, und speichern Sie die Änderungen an der Szene.
Für den letzten Schritt der anfänglichen Testeinrichtung gehen Sie zu Datei > Erstellungseinstellungen und wählen Sie Offene Szenen hinzufügen, um die Szene Scenes/SimpleTesting zu den Erstellungseinstellungen hinzuzufügen.
Wählen Sie den Ordner Tests im Ordner Projekt-Assets. Klicken Sie mit der rechten Maustaste und wählen Sie Erstellen > Testen > C# Testskript.
Benennen Sie das neue Skript CharacterTests. Öffnen Sie das Skript in Ihrer IDE, um es sich genauer anzusehen.
Zwei Methoden-Stubs werden mit der ursprünglichen Klassendatei geliefert, um einige Testgrundlagen zu demonstrieren.
Als Nächstes müssen Sie sicherstellen, dass die Tests eine "testorientierte" Spielszene laden. Dies sollte eine Szene sein, die nur das Nötigste enthält, um das System oder die Komponente, auf die Sie sich konzentrieren, zu testen.
Aktualisieren Sie die Klasse CharacterTests, um zwei neue using-Anweisungen hinzuzufügen, und implementieren Sie die Klasse InputTestFixture:
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class CharacterTests : InputTestFixture
Fügen Sie oben in der Klasse CharacterTests zwei private Felder hinzu:
GameObject character = Resources.Load<GameObject>("Character");
Tastatur;
Im Zeichenfeld wird ein Verweis auf die Zeichenvorlage gespeichert, die aus dem Ordner Ressourcen geladen wurde. Keyboard enthält einen Verweis auf das vom InputSystem bereitgestellte Tastatur-Eingabegerät.
Überschreiben Sie die Setup()-Methode der InputTestFixture-Basisklasse, indem Sie Ihre eigene Methode in der CharacterTests-Klasse bereitstellen:
public override void Setup()
{
SceneManager.LoadScene("Scenes/SimpleTesting");
base.Setup();
keyboard = InputSystem.AddDevice<Keyboard>();
var mouse = InputSystem.AddDevice<Mouse>();
Press(mouse.rightButton);
Release(mouse.rightButton);;
}
Die Setup()-Methode führt die Setup()-Methode der Basisklasse aus und richtet dann Ihre eigene CharacterTests-Klasse ein, indem sie die Testszene lädt und das Tastatureingabegerät initialisiert.
Die Mauseingabe wird nur hinzugefügt, damit der Third Person Controller Eingaben von der simulierten/virtuellen Tastatur empfangen kann. Dies ist fast so etwas wie eine "Fokus setzen"-Aktion.
Für Ihren ersten Test instanziieren Sie die Figur aus der Prefab und stellen sicher, dass sie nicht null ist. Fügen Sie die folgende Methode zu Ihrer Testklasse hinzu:
[Test]
public void TestPlayerInstantiation()
{
GameObject characterInstance = GameObject.Instantiate(character, Vector3.zero, Quaternion.identity);
Assert.That(characterInstance, !Is.Null);
}
Wenn Sie schon dabei sind, sollten Sie die Testmethoden der Mustervorlage bereinigen. Entfernen Sie die Methoden CharacterTestsSimplePasses und CharacterTestsWithEnumeratorPasses.
Speichern Sie das Skript und kehren Sie zum Fenster Test Runner im Editor zurück. Markieren Sie den Test TestPlayerInstantiation und klicken Sie auf Ausgewählte ausführen.
Das grüne Häkchen bedeutet, dass der Test bestanden wurde. Sie haben behauptet, dass die Figur aus Ressourcen geladen und in der Testszene instanziiert werden kann und zu diesem Zeitpunkt nicht null ist.
Sie haben vielleicht bemerkt, dass für diesen Test die Anmerkung [Test] anstelle der Anmerkung [UnityTest] verwendet wurde. Das UnityTest-Attribut ermöglicht es Coroutines, Tests über mehrere Frames laufen zu lassen. In diesem Fall müssen Sie nur das Zeichen instanziieren und bestätigen, dass es geladen wurde.
Im Allgemeinen sollten Sie im Bearbeitungsmodus das NUnit-Test-Attribut anstelle des UnityTest-Attributs verwenden, es sei denn, Sie müssen spezielle Anweisungen geben, einen Frame überspringen oder im Wiedergabemodus eine bestimmte Zeitspanne abwarten.
Als Nächstes verwenden Sie den UnityTest, um festzustellen, dass die Figur bei gedrückter Controllertaste vorwärts läuft.
Fügen Sie die unten angegebene neue Testmethode zu Ihrer Klasse CharacterTests hinzu.
Es gibt zwei neue Testhilfsmethoden: Press() und Release(). Beide werden von der InputTestFixture-Basisklasse bereitgestellt und helfen Ihnen, indem sie das Drücken und Loslassen von InputSystem-Steuerelementen emulieren.
Die Methode TestPlayerMoves() bewirkt Folgendes:
Instanziert eine Instanz des Charakters aus der Charakter Prefab an der Stelle (X: 0, Y: 0, Z: 0)
Drückt die Pfeiltaste nach oben auf der virtuellen Tastatur für 1 Sekunde und lässt sie dann los
Wartet 1 weitere Sekunde (bis die Figur langsamer wird und sich nicht mehr bewegt)
Bestätigt, dass sich das Zeichen an eine Position auf der Z-Achse bewegt hat, die größer als 1,5 Einheiten ist.
Speichern Sie die Datei, kehren Sie zum Test Runner zurück, und führen Sie den neuen Test aus.
Als Nächstes werden Sie ein benutzerdefiniertes Monobehaviour-Skript testen, indem Sie eine einfache Player Health-Komponente hinzufügen.
Erstellen Sie ein neues Skript unter Assets/StarterAssets/ThirdPersonController/Scripts. Nennen Sie es PlayerHealth.
Öffnen Sie das Skript in Ihrer IDE und ersetzen Sie den Inhalt durch den unten angegebenen Code.
Hier wurde eine Menge neuer Code hinzugefügt. Zusammenfassend kann man sagen, dass dieses Skript feststellt, ob sich der Spielercharakter in einem fallenden Zustand befindet. Wenn der Boden einmal im Fall getroffen wird, wird die Gesundheit des Charakters um 10% reduziert.
Suchen Sie die Charaktervorlage unter Assets/Ressourcen. Öffnen Sie die Voreinstellung und fügen Sie die neue PlayerHealth-Skriptkomponente hinzu.
Als Nächstes verwenden Sie die Testszene, um festzustellen, dass die Gesundheit des Spielers sinkt, wenn er von einem Vorsprung fällt.
Mit dem Attribut [UnityTest] können Sie einen Test für den Spielmodus schreiben, der auf Fallschaden testet. Wenn der Spieler länger als 0,2 Sekunden fällt, sollte er 0,1f Schaden erleiden (das entspricht 10% der maximalen Gesundheit).
In der SimpleTesting-Szene siehst du eine Treppe, die zu einem Vorsprung führt. Dies ist eine Testplattform, auf der der Charakter spawnen und das PlayerHealth-Skript testen kann.
Öffnen Sie CharacterTests.cs erneut und fügen Sie eine neue Testmethode namens TestPlayerFallDamage hinzu:
[UnityTest]
public IEnumerator TestPlayerFallDamage()
{
// die Figur in einem ausreichend hohen Bereich der Testszene spawnen
GameObject characterInstance = GameObject.Instantiate(character, new Vector3(0f, 4f, 17.2f), Quaternion.identity);
// Hole einen Verweis auf die PlayerHealth-Komponente und stelle fest, dass der Spieler derzeit volle Gesundheit hat (1f)
var characterHealth = characterInstance.GetComponent<PlayerHealth>();
Assert.That(characterHealth.Health, Is.EqualTo(1f));
// Von der Kante gehen und auf den Sturz warten
Press(keyboard.upArrowKey);
yield return new WaitForSeconds(0.5f);
Release(keyboard.upArrowKey);
yield return new WaitForSeconds(2f);
// Behaupten, dass 1 Lebenspunkt durch den Sturzschaden verloren gegangen ist
Assert.That(characterHealth.Health, Is.EqualTo(0.9f));
}
Außerdem müssen Sie ganz oben in der Klassendatei einen Verweis auf den Namespace StarterAssets hinzufügen:
using StarterAssets;
Der obige Test folgt einem typischen Arranger-Act-Assert-Muster (AAA), das in der Testpraxis häufig vorkommt:
Der Abschnitt Anordnen einer Unit-Test-Methode initialisiert Objekte und setzt den Wert der Daten, die an die zu testende Methode übergeben werden.
Der Abschnitt Act ruft die zu prüfende Methode mit den vereinbarten Parametern auf. In diesem Fall wird der Aufruf der zu testenden Methode durch eine physikalische Interaktion ausgeführt, wenn der Spieler nach einem Sturz auf den Boden aufschlägt.
Der Abschnitt " Assert" überprüft, ob sich die Aktion der zu testenden Methode wie erwartet verhält.
Zurück im Editor führen Sie den neuen Test durch. Wenn Sie im Spielmodus laufen, sehen Sie, wie die Figur über die Kante läuft, fällt (die 0,2-Sekunden-Grenze für einen Sturz wird überschritten) und beim Aufprall auf dem Boden Schaden nimmt.
Tests dienen nicht nur dazu, zu prüfen, ob Codeänderungen die Funktionalität beeinträchtigen, sondern sie können auch als Dokumentation oder als Anhaltspunkte dienen, die den Entwicklern helfen, andere Aspekte des Spiels zu berücksichtigen, wenn sie die Einstellungen optimieren.
Sobald Sie mit der Erstellung einer Testreihe begonnen haben, besteht der nächste Schritt darin, sie nach Abschluss der Erstellung automatisch auszuführen. Automatisierte Unit- und Integrationstests, die nach dem Build ausgeführt werden, sind nützlich, um Regressionen oder Fehler so früh wie möglich zu erkennen. Sie können auch als Teil eines automatisierten Remote-Build-Systems in der Cloud ausgeführt werden.
Oftmals möchten Sie die Ergebnisse von Testläufen in einem benutzerdefinierten Format erfassen, damit die Ergebnisse einem breiteren Publikum zugänglich gemacht werden können. Um Testergebnisse außerhalb des Unity-Editors zu erfassen, müssen Sie die Build- und Run-Prozesse aufteilen.
Erstellen Sie ein neues Skript in Ihrem Projektordner Tests mit dem Namen SetupPlaymodeTestPlayer.
Die Klasse SetupPlaymodeTestPlayer wird die Schnittstelle ITestPlayerBuildModifier implementieren. Damit können Sie die ModifyOptions-Methode außer Kraft setzen und einhaken", die die Player-Optionen des Builds empfängt und es Ihnen ermöglicht, sie zu ändern.
using System.IO;
using UnityEditor;
using UnityEditor.TestTools;
[Montage: TestPlayerBuildModifier(typeof(SetupPlaymodeTestPlayer))]
public class SetupPlaymodeTestPlayer : ITestPlayerBuildModifier
{
public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
{
playerOptions.options &= ~(BuildOptions.AutoRunPlayer | BuildOptions.ConnectToHost);
var buildLocation = Path.GetFullPath("TestPlayers");
var fileName = Path.GetFileName(playerOptions.locationPathName);
if (!string.IsNullOrEmpty(fileName))
buildLocation = Path.Combine(buildLocation, fileName);
playerOptions.locationPathName = buildLocation;
return playerOptions;
}
}
Dieses benutzerdefinierte Player Build Modifier-Skript bewirkt Folgendes, wenn die Tests im Spielmodus ausgeführt werden (Ausführungsort: Am Spieler):
Deaktiviert die automatische Ausführung für erstellte Player und überspringt die Playeroption, die versucht, eine Verbindung mit dem Host herzustellen, auf dem sie ausgeführt wird
Ändert den Speicherort des Erstellungspfads auf einen bestimmten Pfad innerhalb des Projekts(TestPlayers)
Jetzt können Sie davon ausgehen, dass sich die Builds im Ordner TestPlayers befinden, sobald sie fertiggestellt sind. Damit sind die Änderungen am Build abgeschlossen und die Verbindung zwischen Build und Run wird aufgehoben.
Als Nächstes werden Sie die Ergebnisberichterstattung implementieren. So können Sie Testergebnisse an einen benutzerdefinierten Speicherort schreiben, der für die automatische Berichterstellung und Veröffentlichung bereit ist.
Erstellen Sie ein neues Skript in Ihrem Projektordner Tests mit dem Namen ResultSerializer (siehe unten). Diese Klasse verwendet einen Assembly-Verweis auf TestRunCallback und implementiert die Schnittstelle ITestRunCallback.
Diese Implementierung von ITestRunCallback enthält eine benutzerdefinierte RunFinished-Methode, mit der ein Player-Build mit Tests eingerichtet wird, um die Testergebnisse in eine XML-Datei namens testresults.xml zu schreiben.
Durch die Kombination von SetupPlaymodeTestPlayer.cs und ResultSerializer.cs sind die Build- und Run-Prozesse nun getrennt. Bei der Ausführung von Tests werden die Ergebnisse in testresults.xml ausgegeben, das sich im Application. persistentDataPath-Speicher der Player-Plattform befindet.
Um einige der Typen in diesen Hook-Klassen zu verwenden, müssen Sie einen zusätzlichen Verweis auf Tests.asmdef hinzufügen. Aktualisieren Sie sie, um den Verweis auf die UnityEditor.UI.EditorTests Assembly-Definition hinzuzufügen.
Die Ausführung der Tests im Player führt nun zu einer Player-Build-Ausgabe unter Ihrem Projekt im Ordner TestPlayers und zu einer Datei testresults.xml im Speicherort Application.persistentDataPath.
Unity Test Framework Kurs
Das Test Framework-Paket enthält einen Testkurs mit Beispielübungen, die Ihnen helfen, mehr über das Testen mit Unity zu erfahren. Stellen Sie sicher, dass Sie die Projektdateien für den Kurs mit dem Paketmanager herunterladen.
Verwenden der Paketverwaltung > Pakete: Unity Registry > Test Framework, suchen Sie die Dropdown-Liste Samples und importieren Sie die Kursübungen.
Die Übungen werden in Ihr Projekt importiert und befinden sich unter Assets/Samples/Test Framework. Zu jedem Beispiel gehört eine Übungsmappe, mit der Sie arbeiten können, sowie eine Lösung, mit der Sie Ihre eigene Arbeit vergleichen können, während Sie weiterarbeiten.
QS für Ihren Code mit UTF
Dieser Unite Copenhagen-Vortrag über UTF geht mehr ins Detail und bietet einige weitere interessante Anwendungsfälle für die Testanpassung. Schauen Sie sich unbedingt an, was sonst noch möglich ist.
Fehlerbehebung in Unity
Beschleunigen Sie Ihren Debugging-Workflow in Unity mit Artikeln über:
- Microsoft Visual Studio 2022
- Microsoft Visual Studio-Code
Fortgeschrittene technische E-Books
Unity bietet eine Reihe von fortgeschrittenen Anleitungen, die professionellen Entwicklern bei der Optimierung des Spielcodes helfen. Erstellen Sie einen C# Style Guide: Schreiben Sie sauberen, skalierbaren Code stellt Ratschläge von Branchenexperten zusammen, wie Sie einen Code-Style-Guide erstellen können, um Ihr Team bei der Entwicklung einer sauberen, lesbaren und skalierbaren Codebasis zu unterstützen.
Ein weiterer beliebter Leitfaden bei unseren Nutzern ist 70+ Tipps zur Steigerung der Produktivität mit Unity. Es ist vollgepackt mit zeitsparenden Tipps, um Ihren täglichen Arbeitsablauf mit Unity 2020 LTS zu verbessern, einschließlich Tipps, die selbst erfahrene Entwickler vielleicht übersehen haben.
Dokumentation
Erforschen Sie die neueste TestRunner-API weiter, lernen Sie andere UTF Custom-Attribute kennen und entdecken Sie weitere Lebenszyklen, die Sie mit der UTF-Dokumentation einbinden können.
Alle fortgeschrittenen E-Books und Artikel von Unity finden Sie im Unity Best Practices Hub.