Zuletzt aktualisiert: Dezember 2019, Lesezeit: 6 Min.

Testen Sie Ihren Spielcode mit dem Unity Test Framework

Das finden Sie auf dieser Seite: Tipps zur Verwendung des Unity Test Framework (UTF) zur Qualitätssicherung Ihrer Projekte. Das UTF ist eines der wichtigsten Tools für die Qualitätssicherung. Es wird verwendet, um automatisierte Tests sowohl im Editor als auch auf unterstützten Plattformen durchzuführen. Es ist außerdem für alle Unity-Nutzer verfügbar!  

Eine neue Test Runner-API bringt für die meisten Ihrer Testanforderungen größere Flexibilität und Erweiterbarkeit in das UTF. Das Framework ist über den Unity Package Manager verfügbar, sodass wir Fehlerbehebungen und neue Updates schneller liefern können. Das bedeutet auch, dass Sie lokal auf den UTF-Quellcode zugreifen können. Sie können sich den Quellcode ansehen, ihn beim Debuggen durchgehen und modifizieren.  

Einen vollständigen Überblick erhalten Sie im Video der UTF-Session auf der Unite in Kopenhagen, auf der die Unity-Tool- und Testentwickler Richard Fine und Christian Warnecke anwesend waren.

Testen Sie Ihren Spielcode mit dem Unity Test Framework

Erste Schritte mit dem Unity Test Framework

Wenn Ihnen das Unity Test Framework (UTF) neu ist, lesen Sie die Dokumentation zur Einführung. Kurz gesagt, das UTF ermöglicht es Unity-Benutzern, ihren Code sowohl im Bearbeitungs- als auch im Wiedergabemodus und außerdem auf Zielplattformen wie Standalone, Android, iOS und anderen zu testen.

UTF verwendet eine Unity-Integration der NUnit-Bibliothek, die eine Open-Source-Komponententestbibliothek für .Net-Sprachen ist. Mehr Informationen über NUnit finden Sie auf der offiziellen NUnit-Website und in der NUnit-Dokumentation auf GitHub

Neueinsteiger werden diese Blog-Einträge informativ finden:

Leistungsbenchmarking in Unity: Erste Schritte

Testen der testgestützten Entwicklung mit dem Unity Test Runner

Übersicht über die Test Runner-API

Sie können Ihre Tests programmatisch von jedem Skript aus über die Test Runner-API (siehe API weiter unten) ausführen. Sie ermöglicht es Ihnen, eine Liste von Tests abzurufen, die im Bearbeitungsmodus, im Wiedergabemodus oder in beiden Modi laufen, ohne sie auszuführen. Sie können sich zu Beginn und am Ende jedes Tests und auf jeder Ebene innerhalb des Testzyklus, d. h. auf der gesamten Testanordnung, auf jeder einzelnen Testvorrichtung und auf jeder Testklasse und jedem Test, mit einigen registrierten oder nicht registrierten Callbacks verbinden. 

Zu Beginn jedes Tests erhalten Sie Informationen über die Testroute, die kurz vor der Durchführung steht. Nach Abschluss des Tests sehen Sie die Testergebnisse. 

Zusätzlich zum Ausführen des UTF im Wiedergabemodus im Unity-Editor ermöglicht ein neuer Anpassungspunkt die Ausführung auf Zielgeräten. Dieser wird vor der Erstellung des Players aufgerufen. Sie können die Optionen für die Erstellung des Players ändern, um z. B. die Einstellungen für den Testlauf zu ändern und die Erstellungsorte festzulegen.

TestRunnerAPI.cs (C#)
void Execute(ExecutionSettings executionSettings);
void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks;
void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks;
void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback);

Erstellung und Ausführung aufteilen

Die Aufteilung des Erstellungs- und Ausführungsprozesses ist nützlich, wenn Sie Tests auf einem Zielgerät ausführen möchten, das nicht an Ihren lokalen Rechner angeschlossen ist, z. B. wenn es sich in der Cloud befindet (oder mehrere Geräte in der Cloud sind). 

Dazu müssen Sie zunächst den Erstellungsprozess des Test-Players selbst anpassen. So führen Sie diese Anpassung aus: 

  • Deaktivieren Sie die AutoRun-Funktion, sodass der Player nach der Erstellung nicht mehr startet und die Tests ausführt. 
  • Speichern Sie sie an einem bekannten Ort und nicht im temporären Ordner des Systems (wo sie standardmäßig gespeichert würde). 

Fügen Sie dann benutzerdefinierte Ergebnisberichte auf der Player-Seite hinzu (unter Verwendung der Callback-Schnittstelle), um alle Ergebnisse zu erfassen und in einer XML-Datei oder einem anderen für Ihr Projekt geeigneten Format zu speichern. 

In den folgenden Codebeispielen finden Sie Beispiele für die Aufteilung von Erstellung und Ausführung. In der Unite-Session (bei Minute 6:28) führt Richard Fine Sie Schritt für Schritt durch den Code für beide Teile dieser Anwendung – die Erstellung und den Ergebnisbericht.

Erstellung und Ausführung aufteilen: Erstellung

Erstellung:

 

SplitBuildAndRun:build.cs (C#)
[assembly: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;
   }
}

Erstellung und Ausführung aufteilen: Ergebnis in der Ausführung speichern

Ausführung: 

SplitBuildAndRun:SaveResults.cs (C#)
[assembly:TestRunCallback(typeof(ResultSerializer))]
public class ResultSerializer : ITestRunCallback {
   public void RunStarted(ITest testsToRun) { }
   public void TestStarted(ITest test) { }
   public void TestFinished(ITestResult result) { }
   public void RunFinished(ITestResult testResults) {     
   	var path = Path.Combine(Application.persistentDataPath, "testresults.xml");
   	using (var xw = XmlWriter.Create(path, new XmlWriterSettings{Indent = true}))
       	testResults.ToXml(true).WriteTo(xw);
   	System.Console.WriteLine($"***\n\nTEST RESULTS WRITTEN TO\n\n\t{path}\n\n***");
   	Application.Quit(testResults.FailCount > 0 ? 1 : 0);
   }
}

Spezifische Tests von einem Menüelement aus starten

Wenn Entwickler Validierungen schreiben, werden die Ergebnisse oft im Konsolen-Fenster angezeigt, wo sie leicht im Nachrichtenfluss verloren gehen können. Wie wäre es, wenn Sie stattdessen Validierungsergebnisse in einer Testsuite mit klaren Indikatoren an einem dafür vorgesehenen Platz im Editor erhalten könnten? Dies können Sie tun, indem Sie bestimmte Tests für ein Menüelement starten.  

Sie beginnen mit einer Methode, die mit „MenuItem“ verknüpft ist, genau so, wie Sie es mit jedem anderen Editor-Erweiterungs-Menüelement tun würden. Die Methode erstellt ein ScriptableObject-Callback-Objekt. Sie verwenden ein ScriptableObject anstelle einer gewöhnlichen Klasse, sodass die Tests z. B. ein Neuladen der Domäne bewirken können, während die Callbacks intakt bleiben. Wenn das ScriptableObject aktiviert ist, wird es für Callbacks registriert, und wenn es deaktiviert ist, wird es nicht registriert. 

Dann richten Sie Filter ein. Wenn Sie einen Test für ein MenuItem starten, möchten Sie oft auch Tests für eine bestimmte Kategorie oder Gruppe durchführen. Mit Filtern können Sie den Testlauf asynchron ausführen. Er läuft über mehrere Frames hinweg, sodass UnityTest, UnitySetUp und UnityTearDown die Engine-Schleife während des Tests abhaken können. Wenn der Test beendet ist und RunFinished registriert, können Sie festlegen, dass eine Meldung angezeigt oder ein Ergebnisfenster geöffnet wird – was immer für Ihren Arbeitsablauf sinnvoll ist.

Siehe das Codebeispiel unten. Richard Fine erläutert in der Unite-Session die Code-Einrichtung für diese Anwendung (bei Minute 11:50).  

LaunchTestsFromMenuItem.cs (C#)
public class RunTestsFromMenu : ScriptableObject, ICallbacks {
   [MenuItem(“Tools/Run useful tests”)] public static void DoRunTests() {
   	CreateInstance<RunTestsFromMenu>().StartTestRun();
   }
   private void StartTestRun() {
   	hideFlags = HideFlags.HideAndDontSave;
   	CreateInstance<TestRunnerApi>().Execute(new ExecutionSettings {
       	filters = new [] { new Filter{ categoryNames = new[] { “UsefulTests” }}}
   	});
   }
   public void OnEnable() { CreateInstance<TestRunnerApi>().RegisterCallbacks(this); }
   public void OnDisable() { CreateInstance<TestRunnerApi>().UnregisterCallbacks(this); }
   /* ...RunStarted, TestStarted, TestFinished... */
   public void RunFinished(ITestResultAdaptor result) {       
   	…
   	DestroyImmediate(this);
   }

Ausführen von Tests vor dem Erstellen

Das Ausführen von Tests vor der Erstellung kann knifflig sein, da der Build-Prozess erfordert, dass die Tests über einen Callback ausgeführt werden, sodass es keine Möglichkeit gibt, die Engine-Updateschleife zu fördern. Aber der Vorteil ist, dass Sie überprüfen können, ob die Grundfunktionalität funktioniert, bevor Sie Zeit mit der eigentlichen Erstellung verbringen (was bei manchen Projekten viele Minuten dauern kann). 

Sie können diese Anwendung über die Schnittstelle „IPreprocessBuildWithReport“ implementieren, genauso wie Sie jede andere Art der Build-Vorverarbeitung implementieren können. Um die Ergebnisse abzurufen, registrieren Sie wie gewohnt einen Callback.

Da Sie nicht mitten in einem Build in den Wiedergabemodus wechseln können, können Sie die Test Runner-API verwenden, um bestimmte Tests im Bearbeitungsmodus auszuführen. Sie können diese Tests durch Filtern nach Kategorie auswählen, z. B. nach einer vordefinierten Validierungstestkategorie. Sie können diese Tests synchron ausführen. 

Sobald der Test beendet ist, überprüfen Sie die Ergebnisse. Wenn etwas fehlgeschlagen ist, können Sie eine BuildFailed-Ausnahme auslösen, wodurch der Erstellungsprozess abgebrochen wird. 

Diese Anwendung lässt sich in zwei Teile unterteilen: den ResultCollector und den Preprocessor, die Richard Fine im Vortrag (bei Minute 15:20) näher erläutert. 

Schauen Sie Christian Warnecke und Richard Fine bei der Live-Demo der Test Runner-API zu. Sehen Sie sich die gesamte Session an, um noch mehr Tipps zur Qualitätssicherung zu erhalten! 

RunTestsBeforeBuild.cs (C#)
   public ITestResultAdaptor Result { get; private set; }     
   public void RunStarted(ITestAdaptor testsToRun) { }
   public void TestStarted(ITestAdaptor test) { }
   public void TestFinished(ITestResultAdaptor result) { }
   public void RunFinished(ITestResultAdaptor result)
   {
   	Result = result;
   }
}

Haben Ihnen diese Inhalte gefallen?

Wir verwenden Cookies, damit wir Ihnen die beste Erfahrung auf unserer Website bieten können. In unseren Cookie-Richtlinien erhalten Sie weitere Informationen.

Verstanden