Einführung in Unit Testing in Unity

Bild

Sind Sie neugierig, wie Unit-Tests in Unity funktionieren? Sie sind sich nicht sicher, was Unit-Tests im Allgemeinen sind? Wenn Sie diese Fragen positiv beantwortet haben, ist dieses Tutorial hilfreich für Sie. Daraus lernen Sie Folgendes über Unit-Tests:

  • Was ist das
  • Sein Nutzen
  • Vor- und Nachteile
  • So funktioniert es in Unity mit Test Runner
  • Schreiben und Ausführen von zu testenden Komponententests

Hinweis : In diesem Lernprogramm wird davon ausgegangen, dass Sie mit der C # -Sprache und den Grundlagen der Entwicklung in Unity vertraut sind. Wenn Sie Unity noch nicht kennen, lesen Sie zuerst die anderen Tutorials zu dieser Engine .

Was ist ein Unit Test?


Bevor Sie sich mit dem Code befassen, ist es wichtig, ein klares Verständnis der Unit-Tests zu erlangen. Einfach ausgedrückt, Unit-Tests testen ... Einheiten.

Der Komponententest dient (idealerweise) zum Testen einer separaten Codeeinheit. Die Zusammensetzung einer „Einheit“ kann variieren, es ist jedoch wichtig zu beachten, dass beim Testen von Einheiten jeweils genau ein „Element“ getestet werden muss.

Unit-Tests müssen erstellt werden, um sicherzustellen, dass ein kleiner logischer Code in einem bestimmten Szenario genau so ausgeführt wird, wie Sie es erwarten. Dies kann schwierig zu verstehen sein, bevor Sie mit dem Schreiben Ihrer eigenen Komponententests beginnen. Schauen wir uns also ein Beispiel an:

Sie haben eine Methode geschrieben, mit der der Benutzer einen Namen eingeben kann. Die Methode ist so geschrieben, dass Zahlen im Namen nicht zulässig sind und der Name selbst nur aus zehn oder weniger Zeichen bestehen kann. Ihre Methode fängt den Tastenanschlag jeder Taste ab und fügt dem Namensfeld das entsprechende Zeichen hinzu:

 public string name = "" public void UpdateNameWithCharacter(char: character) { // 1 if (!Char.IsLetter(char)) { return; } // 2 if (name.Length > 10) { return; } // 3 name += character; } 

Was ist hier los:

  1. Wenn das Zeichen kein Buchstabe ist, beendet der Code die Funktion vorab und fügt das Zeichen nicht zur Zeichenfolge hinzu.
  2. Wenn der Name zehn oder mehr Zeichen lang ist, kann der Benutzer mit dem Code kein weiteres Zeichen hinzufügen.
  3. Wenn diese beiden Prüfungen bestanden werden, fügt der Code am Ende des Namens ein Zeichen hinzu.

Dieses Gerät kann getestet werden, da es ein "Modul" der durchgeführten Arbeit ist. Unit-Tests erzwingen Methodenlogik.

Unit Test Beispiel


Wie schreiben wir Unit-Tests für die UpdateNameWithCharacter Methode?

Bevor wir mit der Implementierung dieser Komponententests beginnen, müssen wir sorgfältig überlegen, was diese Tests bewirken, und Namen für sie finden.

Schauen Sie sich die folgenden Beispiele für Unit-Testnamen an. Aus den Namen sollte klar sein, dass sie überprüfen:

UpdateNameDoesntAllowCharacterAddingToNameIfNameIsTenOrMoreCharactersInLength

UpdateNameAllowsLettersToBeAddedToName

UpdateNameDoesntAllowNonLettersToBeAddedToName

UpdateNameWithCharacter dieser Namen von Testmethoden sehen wir, dass wir wirklich prüfen, ob die "Arbeitseinheit" von der UpdateNameWithCharacter Methode ausgeführt wird. Diese Testnamen mögen zu lang und detailliert erscheinen, aber es ist gut für uns.

Jeder Unit-Test, den Sie schreiben, ist Teil einer Reihe von Tests. Die Testsuite enthält alle Komponententests, die sich auf die funktionale logische Gruppe beziehen (z. B. „Kampfeinheitentests“). Wenn ein Test aus dem Kit den Test nicht besteht, schlägt die gesamte Testsuite fehl.


Spielstart


Öffnen Sie das Crashteroids Starter-Projekt (Sie können es hier herunterladen) und öffnen Sie dann die Spielszene im Ordner " Assets / RW / Scenes ".


Klicken Sie auf Spielen , um Crashteroids zu starten, und klicken Sie dann auf die Schaltfläche Spiel starten . Bewegen Sie das Raumschiff mit den Pfeilen nach links und rechts auf der Tastatur.

Drücken Sie die Leertaste, um einen Laserstrahl abzufeuern . Wenn der Strahl auf den Asteroiden trifft, erhöht sich die Punktzahl um eins. Wenn ein Asteroid mit einem Schiff kollidiert, explodiert das Schiff und das Spiel endet (mit der Fähigkeit, erneut zu starten).


Versuchen Sie ein wenig zu spielen und stellen Sie sicher, dass nach der Kollision des Asteroiden mit dem Schiff die Inschrift Game Over erscheint.


Erste Schritte mit Unity Test Runner


Jetzt, da wir wissen, wie das Spiel läuft, ist es Zeit, Unit-Tests zu schreiben, um zu überprüfen, ob alles so funktioniert, wie es sollte. Wenn Sie (oder jemand anderes) sich entscheiden, das Spiel zu aktualisieren, werden Sie sicher sein, dass das Update nichts kaputt macht, was zuvor funktioniert hat.

Um Tests zu schreiben, müssen Sie zunächst den Unity Test Runner kennenlernen. Mit Test Runner können Sie Tests ausführen und prüfen, ob sie erfolgreich bestanden wurden. Um den Unity Test Runner zu öffnen, wählen Sie Fenster ▸ Allgemein ▸ Test Runner .


Nachdem der Test Runner in einem neuen Fenster geöffnet wurde, können Sie Ihr Leben vereinfachen, indem Sie auf das Test Runner-Fenster klicken und es an die Stelle neben dem Szenenfenster ziehen.


NUnit- und Testordner vorbereiten


Test Runner ist eine Unit-Test-Funktion von Unity, die jedoch das NUnit- Framework verwendet. Wenn Sie anfangen, ernsthafter mit Unit-Tests zu arbeiten, empfehle ich, das Wiki auf NUnit zu studieren, um mehr zu erfahren. Über alles, was Sie zum ersten Mal benötigen, wird in diesem Artikel erläutert.

Um die Tests auszuführen, müssen wir zuerst einen Testordner erstellen, in dem die Testklassen gespeichert werden.

Wählen Sie im Projektfenster den Ordner RW aus . Schauen Sie sich das Test Runner- Fenster an und stellen Sie sicher, dass PlayMode ausgewählt ist.

Klicken Sie auf die Schaltfläche PlayMode-Testassembly-Ordner erstellen . Im RW-Ordner wird ein neuer Ordner angezeigt. Wir sind mit dem Standardnamen Tests zufrieden, Sie können also einfach die Eingabetaste drücken.


Möglicherweise fragen Sie sich, welche zwei verschiedenen Registerkarten sich in Test Runner befinden.

Die Registerkarte PlayMode wird für Tests verwendet, die im Wiedergabemodus durchgeführt werden (wenn das Spiel in Echtzeit ausgeführt wird). Die Tests auf der Registerkarte " EditMode" werden außerhalb des Wiedergabemodus ausgeführt. Dies ist praktisch, um beispielsweise benutzerdefinierte Verhaltensweisen im Inspektor zu testen.

In diesem Tutorial werden die PlayMode-Tests behandelt. Wenn Sie es sich jedoch bequem machen, können Sie versuchen, mit Tests in EditMode zu experimentieren. Stellen Sie bei der Arbeit mit Test Runner in diesem Lernprogramm immer sicher, dass die Registerkarte PlayMode ausgewählt ist .

Was ist in der Testsuite?


Wie wir oben erfahren haben, ist ein Komponententest eine Funktion, die das Verhalten eines kleinen bestimmten Codeteils testet. Da der Komponententest eine Methode ist, muss er in der Klassendatei enthalten sein, damit er ausgeführt werden kann.

Test Runner umgeht alle Testklassendateien und führt daraus Unit-Tests durch. Eine Klassendatei mit Komponententests wird als Testsuite bezeichnet.

In der Testsuite unterteilen wir unsere Tests logisch. Wir müssen den Testcode in separate logische Mengen aufteilen (zum Beispiel eine Reihe von Tests für die Physik und eine separate Menge für den Kampf). In diesem Tutorial benötigen wir nur einen Testsatz, und es ist Zeit, einen zu erstellen.

Vorbereiten einer Testbaugruppe und einer Testsuite


Wählen Sie den Ordner Tests aus und klicken Sie im Fenster Test Runner auf die Schaltfläche Testskript im aktuellen Ordner erstellen . Benennen Sie die neue TestSuite- Datei.


Zusätzlich zur neuen C # -Datei erstellt die Unity-Engine eine weitere Datei mit dem Namen Tests.asmdef . Dies ist die Assemblydefinitionsdatei , mit der Unity angezeigt wird, wo sich die Abhängigkeiten der Testdatei befinden. Dies ist erforderlich, da der fertige Anwendungscode getrennt vom Testcode enthalten ist.

Wenn Unity keine Testdateien oder Tests finden kann, stellen Sie sicher, dass eine Assemblydefinitionsdatei vorhanden ist, die Ihre Testsuite enthält. Der nächste Schritt ist die Konfiguration.

Damit der Testcode Zugriff auf die Spielklassen hat, erstellen wir eine Assembly des Klassencodes und setzen den Link in der Test-Assembly. Klicken Sie auf den Ordner " Skripte ", um ihn auszuwählen. Klicken Sie mit der rechten Maustaste auf diesen Ordner und wählen Sie Erstellen ▸ Baugruppendefinition .


Nennen Sie die Datei GameAssembly .


Klicken Sie auf den Ordner Tests und dann auf die Testdefinitionsdatei . Klicken Sie im Inspektor auf die Schaltfläche Plus unter der Überschrift Assembly Definition References .


Sie sehen das Feld Fehlende Referenz . Klicken Sie auf den Punkt neben diesem Feld, um das Auswahlfenster zu öffnen. Wählen Sie die GameAssembly- Datei aus.


Sie sollten die GameAssembly-Assemblydatei im Abschnitt "Links" sehen. Klicken Sie auf die Schaltfläche Übernehmen , um diese Änderungen zu speichern.


Wenn Sie diese Schritte nicht ausführen, können Sie nicht auf die Klassendateien des Spiels in den Komponententestdateien verweisen. Nachdem Sie sich damit befasst haben, können Sie mit dem Code fortfahren.

Wir schreiben den ersten Unit Test


Doppelklicken Sie auf das TestSuite- Skript, um es im Code-Editor zu öffnen. Ersetzen Sie den gesamten Code durch:

 using UnityEngine; using UnityEngine.TestTools; using NUnit.Framework; using System.Collections; public class TestSuite { } 

Welche Tests müssen wir schreiben? Ehrlich gesagt, selbst in einem winzigen Spiel wie Crashteroids können Sie einige Tests schreiben, um zu überprüfen, ob alles so funktioniert, wie es sollte. In diesem Tutorial beschränken wir uns nur auf Schlüsselbereiche: Kollisionserkennung und grundlegende Spielmechanik.

Hinweis : Wenn Sie Unit-Tests eines Produkts auf Produktionsebene schreiben möchten, sollten Sie genügend Zeit aufwenden, um alle Grenzfälle zu berücksichtigen, die in allen Bereichen des Codes getestet werden müssen.

Als ersten Test ist es schön zu überprüfen, ob sich die Asteroiden wirklich nach unten bewegen. Es wird für sie schwierig sein, mit dem Schiff zu kollidieren, wenn sie sich von ihm entfernen! Fügen Sie dem TestSuite- Skript die folgende Methode und private Variable hinzu:

 private Game game; // 1 [UnityTest] public IEnumerator AsteroidsMoveDown() { // 2 GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); // 3 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); // 4 float initialYPos = asteroid.transform.position.y; // 5 yield return new WaitForSeconds(0.1f); // 6 Assert.Less(asteroid.transform.position.y, initialYPos); // 7 Object.Destroy(game.gameObject); } 

Es gibt nur wenige Codezeilen, aber sie machen viele Dinge. Also lasst uns innehalten und uns mit jedem Teil befassen:

  1. Dies ist ein Attribut . Attribute definieren bestimmte Compilerverhalten. Dieses Attribut teilt dem Unity-Compiler mit, dass der Code ein Komponententest ist. Aus diesem Grund wird es beim Starten der Tests im Test Runner angezeigt.
  2. Erstellen Sie eine Instanz von Game. Alles andere ist in das Spiel eingebettet. Wenn wir es erstellen, enthält es alles, was getestet werden muss. In einer Produktionsumgebung befinden sich höchstwahrscheinlich nicht alle Elemente im selben Fertighaus. Daher müssen Sie alle in der Szene benötigten Objekte neu erstellen.
  3. Hier erstellen wir einen Asteroiden, damit wir überwachen können, ob er sich bewegt. Die SpawnAsteroid Methode gibt eine Instanz des erstellten Asteroiden zurück. Die Asteroid-Komponente verfügt über eine Move Methode (wenn Sie neugierig sind, wie Bewegung funktioniert, können Sie sich das Asteroid- Skript in RW / Scripts ansehen).
  4. Die Verfolgung der Startposition ist erforderlich, um sicherzustellen, dass sich der Asteroid nach unten bewegt hat.
  5. Alle Unity-Komponententests sind Coroutinen, daher müssen Sie eine weiche Rückgabe hinzufügen. Wir fügen außerdem einen Zeitschritt von 0,1 Sekunden hinzu, um den Zeitablauf zu simulieren, für den sich der Asteroid nach unten bewegen sollte. Wenn Sie keinen Zeitschritt simulieren müssen, können Sie null zurückgeben.
  6. Dies ist die Assertionsphase , in der wir behaupten, dass die Position des Asteroiden kleiner ist als die Ausgangsposition (dh er hat sich nach unten bewegt). Das Verständnis von Assertions ist ein wichtiger Bestandteil von Unit-Tests, und NUnit bietet verschiedene Assertion-Methoden. Das Bestehen oder Nichtbestehen des Tests wird durch diese Linie bestimmt.
  7. Natürlich wird Sie niemand für das Chaos schelten, das nach Abschluss der Tests übrig geblieben ist, aber andere Tests können aufgrund dessen fehlschlagen. Es ist immer wichtig, den Code nach dem Komponententest zu bereinigen (zu löschen oder zurückzusetzen), damit beim Ausführen des nächsten Komponententests keine Artefakte mehr vorhanden sind, die diesen Test beeinträchtigen könnten. Es reicht aus, das Spielobjekt einfach zu löschen, da wir für jeden Test eine völlig neue Instanz des Spiels erstellen.

Tests bestehen


Sie haben Ihren ersten Komponententest geschrieben, aber woher wissen Sie, ob er funktioniert? Natürlich mit Test Runner! Erweitern Sie im Fenster Test Runner alle Zeilen mit Pfeilen. Sie sollten den AsteroidsMoveDown Test in der Liste mit grauen Kreisen sehen:


Ein grauer Kreis zeigt an, dass der Test noch nicht abgeschlossen ist. Wenn der Test gestartet und bestanden wurde, wird daneben ein grüner Pfeil angezeigt. Wenn der Test fehlgeschlagen ist, wird ein rotes X daneben angezeigt. Führen Sie den Test aus, indem Sie auf die Schaltfläche RunAll klicken .


Dadurch wird eine temporäre Szene erstellt und der Test ausgeführt. Nach Abschluss sollten Sie sehen, dass der Test bestanden wurde.


Sie haben Ihren ersten Komponententest erfolgreich geschrieben und festgestellt, dass sich die erstellten Asteroiden nach unten bewegen.

Hinweis : Bevor Sie mit dem Schreiben Ihrer eigenen Komponententests beginnen, müssen Sie die Implementierung verstehen, die Sie testen. Wenn Sie neugierig sind, wie die zu testende Logik funktioniert, lesen Sie den Code im Ordner RW / Scripts .

Integrationstests verwenden


Bevor wir uns näher mit dem Kaninchenbau von Unit-Tests befassen, ist es an der Zeit zu erklären, was Integrationstests sind und wie sie sich von Unit-Tests unterscheiden.

Integrationstests sind Tests, die überprüfen, wie die "Module" des Codes zusammenarbeiten. "Modul" ist ein weiterer Fuzzy-Begriff. Ein wichtiger Unterschied besteht darin, dass Integrationstests den Betrieb der Software in der realen Produktion testen müssen (d. H. Wenn ein Spieler wirklich ein Spiel spielt).


Angenommen, Sie haben ein Kampfspiel gemacht, in dem ein Spieler Monster tötet. Sie können einen Integrationstest erstellen, um sicherzustellen, dass ein Erfolg geöffnet wird, wenn ein Spieler 100 Gegner tötet („Erfolg“).

Dieser Test betrifft mehrere Codemodule. Höchstwahrscheinlich handelt es sich dabei um die physische Engine (Erkennung von Kollisionen), feindliche Disponenten (Überwachung der Gesundheit des Feindes und Verarbeitung von Schaden sowie Weitergabe an andere verwandte Ereignisse) und einen Ereignistracker, der alle ausgelösten Ereignisse verfolgt (z. B. „Das Monster wird getötet“). Wenn es Zeit ist, die Leistung freizuschalten, kann er den Leistungsmanager anrufen.

Der Integrationstest simuliert den Spieler, der 100 Monster tötet, und prüft, ob der Erfolg freigeschaltet ist. Es unterscheidet sich stark vom Komponententest, da große Codekomponenten getestet werden, die zusammenarbeiten.

In diesem Tutorial werden keine Integrationstests untersucht, dies sollte jedoch den Unterschied zwischen der Arbeitseinheit (und warum sie einheitlich getestet wird) und dem Modul des Codes (und warum sie integrativ getestet wird) zeigen.

Hinzufügen eines Tests zu einer Testsuite


Der nächste Test testet das Ende des Spiels, wenn das Schiff mit einem Asteroiden kollidiert. Fügen Sie bei geöffneter TestSuite im Code- Editor den unten gezeigten Test unter dem ersten Komponententest hinzu und speichern Sie die Datei:

 [UnityTest] public IEnumerator GameOverOccursOnAsteroidCollision() { GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); Game game = gameGameObject.GetComponent<Game>(); GameObject asteroid = game.GetSpawner().SpawnAsteroid(); //1 asteroid.transform.position = game.GetShip().transform.position; //2 yield return new WaitForSeconds(0.1f); //3 Assert.True(game.isGameOver); Object.Destroy(game.gameObject); } 

Wir haben den größten Teil dieses Codes bereits im vorherigen Test gesehen, aber es gibt einige Unterschiede:

  1. Wir zwingen den Asteroiden und das Schiff zur Kollision und geben dem Asteroiden eindeutig die gleiche Position wie dem Schiff. Dies führt zu einer Kollision ihrer Hitboxen und zum Ende des Spiels. Wenn Sie neugierig sind, wie dieser Code funktioniert, sehen Sie sich die Dateien Ship , Game und Asteroid im Ordner Scripts an.
  2. Für das Auslösen des Kollisionsereignisses des physischen Motors ist ein Zeitschritt erforderlich, sodass eine Verzögerung von 0,1 Sekunden zurückgegeben wird.
  3. Diese Aussage ist wahr und überprüft, ob das gameOver Flag im Game-Skript wahr ist. Die Flagge wird während des Spielbetriebs wahr, wenn das Schiff zerstört wird, dh wir testen, um sicherzustellen, dass sie nach der Zerstörung des Schiffes auf wahr gesetzt wird.

Kehren Sie zum Fenster Test Runner zurück, und Sie werden sehen, dass dort ein neuer Komponententest angezeigt wurde.


Dieses Mal werden wir dieses anstelle der gesamten Testsuite ausführen. Klicken Sie auf GameOverOccursOnAsteroidCollision und dann auf die Schaltfläche Ausgewählte ausführen .


Und voila, wir haben einen weiteren Test bestanden.


Stufen der Abstimmung und Zerstörung


Möglicherweise haben Sie bemerkt, dass sich in unseren beiden Tests Code wiederholt: Wo das Spielobjekt erstellt wird und wo ein Link zum Spielskript festgelegt ist:

 GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); 

Sie werden auch feststellen, dass sich die Zerstörung des Spielobjekts wiederholt:

 Object.Destroy(game.gameObject); 

Beim Testen passiert dies sehr oft. Wenn es darum geht, Unit-Tests durchzuführen, gibt es tatsächlich zwei Phasen: die Setup- Phase und die Tear-Down- Phase.

Der gesamte Code in der Setup-Methode wird vor dem Komponententest (in diesem Satz) ausgeführt, und der gesamte Code in der Tear Down-Methode wird nach dem Komponententest (in diesem Satz) ausgeführt.

Es ist Zeit, unser Leben zu vereinfachen, indem wir das Setup verschieben und den Code auf spezielle Methoden reduzieren. Öffnen Sie den Code-Editor und fügen Sie den folgenden Code am Anfang der TestSuite- Datei direkt vor dem ersten [UnityTest] -Attribut hinzu:

 [SetUp] public void Setup() { GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); } 

Das SetUp Attribut gibt an, dass diese Methode vor jedem SetUp aufgerufen wird.

Fügen Sie dann die folgende Methode hinzu und speichern Sie die Datei:

 [TearDown] public void Teardown() { Object.Destroy(game.gameObject); } 

Das TearDown Attribut gibt an, dass diese Methode nach jedem Testlauf aufgerufen wird.

Nachdem Sie den Setup- und Zerstörungscode vorbereitet haben, löschen Sie die in diesen Methoden vorhandenen Codezeilen und ersetzen Sie sie durch Aufrufe der entsprechenden Methoden. Danach sieht der Code folgendermaßen aus:

 public class TestSuite { private Game game; [SetUp] public void Setup() { GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); } [TearDown] public void Teardown() { Object.Destroy(game.gameObject); } [UnityTest] public IEnumerator AsteroidsMoveDown() { GameObject asteroid = game.GetSpawner().SpawnAsteroid(); float initialYPos = asteroid.transform.position.y; yield return new WaitForSeconds(0.1f); Assert.Less(asteroid.transform.position.y, initialYPos); } [UnityTest] public IEnumerator GameOverOccursOnAsteroidCollision() { GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = game.GetShip().transform.position; yield return new WaitForSeconds(0.1f); Assert.True(game.isGameOver); } } 

Game Over und Laser Shooting testen


Nachdem wir die Abstimmungs- und Zerstörungsmethoden vorbereitet haben, die unser Leben vereinfachen, können wir neue Tests hinzufügen, in denen sie verwendet werden. Der nächste Test besteht darin, zu überprüfen, ob der Wert von gameOver bool nicht wahr ist, wenn ein Spieler auf Neues Spiel klickt. Fügen Sie einen solchen Test am Ende der Datei hinzu und speichern Sie ihn:

 [UnityTest] public IEnumerator NewGameRestartsGame() { //1 game.isGameOver = true; game.NewGame(); //2 Assert.False(game.isGameOver); yield return null; } 

Dies sollte Ihnen bereits bekannt vorkommen, aber Folgendes ist erwähnenswert:

  1. Dieser Code bereitet diesen Test darauf vor, dass das boolesche Flag gameOver wahr ist. Beim Aufrufen der NewGame Methode muss das Flag erneut auf false .
  2. Hier argumentieren wir, dass bool isGameOver false , was beim Aufrufen eines neuen Spiels wahr sein sollte.

Kehren Sie zu Test Runner zurück und Sie sollten sehen, dass es einen neuen Test NewGameRestartsGame gibt . Führen Sie diesen Test wie zuvor aus, und Sie werden sehen, dass er erfolgreich ausgeführt wird:


Laserstrahl-Erklärung


Der nächste Test besteht darin, den Test hinzuzufügen, dass der vom Schiff abgefeuerte Laserstrahl hochfliegt (ähnlich dem ersten Unit-Test, den wir geschrieben haben). Öffnen Sie die TestSuite- Datei im Editor. Fügen Sie die folgende Methode hinzu und speichern Sie die Datei:

 [UnityTest] public IEnumerator LaserMovesUp() { // 1 GameObject laser = game.GetShip().SpawnLaser(); // 2 float initialYPos = laser.transform.position.y; yield return new WaitForSeconds(0.1f); // 3 Assert.Greater(laser.transform.position.y, initialYPos); } 

Dieser Code bewirkt Folgendes:

  1. Ruft eine Verbindung zu dem vom Schiff emittierten Laserstrahl ab.
  2. Die Startposition wird aufgezeichnet, damit wir überprüfen können, ob sie sich nach oben bewegt.
  3. Diese Aussage stimmt mit der Aussage aus dem AsteroidsMoveDown Komponententest überein. Erst jetzt behaupten wir, dass der Wert größer ist (dh der Laser bewegt sich nach oben).

Speichern Sie die Datei und kehren Sie zu Test Runner zurück. Führen Sie den LaserMovesUp- Test aus und beobachten Sie, wie er erfolgreich ist:


Jetzt sollten Sie bereits verstehen, wie alles funktioniert. Fügen Sie also die letzten beiden Tests hinzu und schließen Sie das Lernprogramm ab.

Überprüfen, ob der Laser Asteroiden zerstört


Als nächstes werden wir sicherstellen, dass der Laser den Asteroiden zerstört, wenn er getroffen wird. Öffnen Sie den Editor, fügen Sie am Ende von TestSuite den folgenden Test hinzu und speichern Sie die Datei:

 [UnityTest] public IEnumerator LaserDestroysAsteroid() { // 1 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = Vector3.zero; GameObject laser = game.GetShip().SpawnLaser(); laser.transform.position = Vector3.zero; yield return new WaitForSeconds(0.1f); // 2 UnityEngine.Assertions.Assert.IsNull(asteroid); } 

So funktioniert es:

  1. Wir erzeugen einen Asteroiden und einen Laserstrahl und weisen ihnen dieselbe Position zu, um eine Kollision auszulösen.
  2. Dies ist ein spezieller Test mit einer wichtigen Unterscheidung. Sehen Sie, dass wir UnityEngine.Assertions explizit für diesen Test verwenden ? Dies liegt daran, dass Unity eine spezielle Null- Klasse hat , die sich von der „regulären“ Null-Klasse unterscheidet. Eine NUnit-Framework-Anweisung Assert.IsNull() funktioniert bei Unity-Prüfungen auf Null nicht. Wenn Sie in Unity nach Null suchen, müssen Sie UnityEngine.Assertions.Assert explizit verwenden, nicht Assert from NUnit.

Kehren Sie zu Test Runner zurück und führen Sie einen neuen Test aus. Sie werden ein grünes Symbol sehen, das uns gefällt.


Testen oder nicht testen - das ist die Frage


Die Entscheidung, sich an Unit-Tests zu halten, ist keine leichte Entscheidung und sollte nicht leichtfertig getroffen werden. Die Vorteile von Tests sind jedoch die Mühe wert. Es gibt sogar eine Entwicklungsmethodik, die so genannte testgetriebene Entwicklung (Test Driven Development, TDD).

Im Rahmen von TDD schreiben Sie Tests, bevor Sie die Anwendungslogik selbst schreiben. Zuerst erstellen Sie Tests, stellen sicher, dass das Programm sie nicht besteht, und schreiben dann nur Code, der die Tests bestehen soll. Dies mag ein ganz anderer Ansatz für die Codierung sein, stellt jedoch sicher, dass Sie den Code auf eine zum Testen geeignete Weise schreiben.

Denken Sie daran, wenn Sie mit der Arbeit an Ihrem nächsten Projekt beginnen. Aber jetzt ist es Zeit, eigene Unit-Tests zu schreiben, für die Sie ein Spiel benötigen, das wir für Sie bereitgestellt haben.

: — , . , . «» , , . , . , . , , .

Das Testen kann eine große Investition sein. Berücksichtigen Sie daher die Vor- und Nachteile des Hinzufügens von Unit-Tests zu Ihrem Projekt:

Unit Testing Vorteile


Unit-Tests haben viele wichtige Vorteile, darunter die folgenden:

  • Es gibt Vertrauen, dass sich die Methode wie erwartet verhält.
  • Dient als Dokumentation für neue Personen, die die Codebasis studieren (Unit-Tests eignen sich hervorragend für den Unterricht).
  • Ermöglicht das Schreiben von Code auf testbare Weise.
  • Ermöglicht das schnellere Isolieren und Beheben von Fehlern.
  • Zukünftige Updates können dem alten Arbeitscode keine neuen Fehler hinzufügen (sie werden als Regressionsfehler bezeichnet).

Nachteile von Unit-Tests


Möglicherweise haben Sie jedoch keine Zeit oder kein Budget für Unit-Tests. Hier sind die Nachteile zu berücksichtigen:

  • Das Schreiben von Tests kann länger dauern als der Code selbst.
  • .
  • .
  • , .
  • , -.
  • ( ), .
  • - .
  • UI .
  • .
  • .

,


Es ist Zeit, den letzten Test zu schreiben. Öffnen Sie den Code-Editor, fügen Sie den folgenden Code am Ende der TestSuite- Datei hinzu und speichern Sie ihn:

 [UnityTest] public IEnumerator DestroyedAsteroidRaisesScore() { // 1 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = Vector3.zero; GameObject laser = game.GetShip().SpawnLaser(); laser.transform.position = Vector3.zero; yield return new WaitForSeconds(0.1f); // 2 Assert.AreEqual(game.score, 1); } 

Dies ist ein wichtiger Test, der bestätigt, dass sich die Punktzahl erhöht, wenn ein Spieler einen Asteroiden zerstört. Daraus besteht es:

  1. Wir erzeugen einen Asteroiden und einen Laserstrahl und bringen sie in eine Position. Aufgrund dessen entsteht eine Kollision, die eine Erhöhung der Punktzahl auslöst.
  2. Die Aussage, dass game.score jetzt 1 ist (und nicht 0, wie es am Anfang war).

Speichern Sie den Code und kehren Sie zu Test Runner zurück, um diesen letzten Test auszuführen und festzustellen, ob das Spiel ausgeführt wird:


Super! Alle Tests bestanden.

Wohin als nächstes?


In dem Artikel haben wir eine große Menge an Informationen untersucht. Wenn Sie Ihre Arbeit mit dem endgültigen Projekt vergleichen möchten, sehen Sie es sich im Archiv an , auf das auch am Anfang des Artikels verwiesen wird.

In diesem Tutorial haben Sie gelernt, was Unit-Tests sind und wie man sie in Unity schreibt. Darüber hinaus haben Sie sechs Komponententests geschrieben, die den Code erfolgreich bestanden haben, und einige der Vor- und Nachteile von Komponententests kennengelernt.

Fühlen Sie sich sicher? Dann können Sie viele weitere Tests schreiben. Untersuchen Sie die Klassendateien des Spiels und versuchen Sie, Komponententests für andere Teile des Codes zu schreiben. Erwägen Sie das Hinzufügen von Tests für die folgenden Szenarien:

  • Jede Art von Asteroiden, wenn Sie das Schiff berühren, führt zum Ende des Spiels.
  • Durch das Starten eines neuen Spiels wird die Punktzahl zurückgesetzt.
  • Die Bewegung nach links und rechts für das Schiff funktioniert korrekt.

Wenn Sie Ihr Wissen über Unit-Tests erweitern möchten, sollten Sie die Implementierung von Abhängigkeiten und Frameworks für die Arbeit mit Scheinobjekten untersuchen . Dies kann den Testaufbau erheblich vereinfachen.

Lesen Sie auch die NUnit-Dokumentation , um mehr über das NUnit-Framework zu erfahren.

Und zögern Sie nicht, Ihre Gedanken und Fragen in den Foren zu teilen.

Erfolgreiches Testen!

Source: https://habr.com/ru/post/de456090/


All Articles