-
Notifications
You must be signed in to change notification settings - Fork 0
Tests
Das Interface CurrentConverter (Implementierung in SadCurrentConverter) stellt Methoden zur Verfügung, die zur Konvertierung digitaler oder analoger Werte anderer Smarthome-Systeme in Minecraft Redstone-Signalstärke (ein ganzzahliger Wert von 0 bis 15) und zurück genutzt werden können. Die Werte anderer Systeme (nachfolgend "Zustand") sind natürliche Zahlen mit angegebener Obergrenze.
Dabei sollen folgende Regeln eingehalten werden:
-
0 wird zu 0
-
NUR 0 wird zu 0
-
der höchstmögliche Zustand wird zur höchstmöglichen Signalstärke und umgekehrt.
-
die restlichen Werte sollen möglichst linear interpoliert werden, ohne die anderen Regeln zu verletzen.
Um die Einhaltung einiger dieser Regeln sicherzustellen, wurden Testfälle geschrieben. Zunächst exisitieren verschiedene Tests, die das Übergeben ungültiger Werte (z.B. negative Werte oder Signalstärken über 15) sowie ungültiger Kombinationen von Werten (ein Zustand, der über dem angegebenen Limit liegt) testen. In diesem Fall wird das Werfen einer Exception erwartet.
Aktuell existieren nur Tests mit digitalen Zuständen. Dabei werden Regel 1 und 3 von vier Tests (2 für je zwei Richtungen) behandelt. Außerdem werden die direkten Nachbarn der Signalstärke 0 und 15, nämlich 1 und 14 sowie ein Wert dazwischen getestet. Dass Regel 4 stimmt, wird also nur angenommen. Basierend auf dieser Annahme lässt sich jedoch die Einhaltung von Regel 2 schlussfolgern, da durch die Prüfung der direkten Nachbarn von 0 und 15 kein Zustand, der wie 0 nicht dazwischen liegt, mehr erreicht werden kann.
Insgesamt wurden hier 16 Tests geschrieben.
Die PluginFactory die im Bridge Programmteil verwendet wird, soll nur Instanzen der Plugins instantiieren, wenn sie das IPlugin Interface implementieren. Die PluginFactory prüft, ob die Klasse dem Interface IPlugin zuordenbar ist.
Wenn sie zuordenbar ist wird eine Instanz der Klasse erstellt.
Um zu testen, ob die createPluginMethode der PluginFactory das erwünschte Verhalten zeigt, wurde in PluginFactoryTest diese Implementierung der PluginFactory mit Unit Tests getestet.
Dabei wurden 2 Testfälle definiert:
-
createPluginmit inkorrekter Klasse aufgerufen -
createPluginmit korrekter Klasse aufgerufen
In den Tests der Core Klasse der Smarthome Bridge werden Elemente verwendet, die per Dependency Injection erwartet werden.
Zum Zeitpunkt als ich diese Tests implementiert habe, wurden nicht alle per Dependency Injection übergebenen Teile modular übergeben.
Sprich, es wurde beispielsweise beim Erstellen der Core Klasse vom Loader immer eine IConfiguration Instanz erwartet. Diese Klassen werden in den Tests mit dem Mockframework Mockito durch Platzhalterklassen ausgetauscht. Die Core Tests testen folgendes Verhalten der Core Klasse.
Desweiteren wird ein ICoreFeature gemockt, da nicht das Verhalten des ICoreFeatures getestet wird sondern das Verhalten der Core Klasse. Da die ICoreFeatures auch die unterschiedlichsten Implementierungen aufweisen können, ist es nicht sinnvoll, sie mit einer konkreten Implementierung zu testen.
- Statusänderung mit
setStateMethode - Rückgabe des korrekten States mit
getState - Test des Startens und Stoppens des Kerns
- Test der erfolgreichen Rückgabe des
ICoreTyps - Test der Registrierung und De-Registrierung von
ICoreFeatures
Zum Hinzufügen und Entfernen von Geräten im Minecraft Smarthome werden Befehle verwendet, die im Spielchat eingegeben werden. Das Interface MinecraftCommandArgumentParser, das von der Klasse SadMinecraftCommandArgumentParser implementiert wird, stellt Methoden zur Konvertierung der Argumente des Befehls zu intern verwendeten Datentypen zur Verfügung. In SadMinecraftCommandArgumentParserTest wird diese Implementierung getestet. Zur Instantiierung des Parsers werden eine BlockIdentifierFactory und eine WorldIdentifierFactory per Dependency Injection benötigt. Beim Aufruf der Methode extractBlockIdentifier wird außerdem eine MinecraftCommandSource als Parameter erwartet. Zum Testen müssen diese Objekte gemockt werden. Diese drei Interfaces werden daher von drei handgeschriebenen Dummy-Klassen in der Test-Klasse implementiert.
Mit der Methode getWorldIdentifier in MinecraftCommandSource kann (durch Prüfung des Optional) erkannt werden, ob der Absender implizit eine Minecraft-Welt spezifiziert (z.B. da er sich darin aufhält). Dies ist der Fall bei Spielern, die einen Befehl ausführen, nicht aber wenn der Befehl von der Server-Konsole kommt. In diesem Fall wird die Angabe der Welt, ein sonst optionaler Parameter des Befehls zum Hinzufügen von Geräten, obligatorisch. Entsprechend gibt es zwei Methoden getDummyCommandSourceNotInAWorld und getDummyCommandSourceInWorldy in der Test-Klasse, die Mock-Objekte erzeugen, welche vorgeben, in einer Welt zu sein oder eben nicht.
Die beiden anderen Dummy-Klassen sind ähnlich umgesetzt und ahmen das Verhalten nach, von dem bei den wirklichen Klassen (die hier ja nicht getestet werden sollen) ausgegangen wird.
Hier wurden 22 Tests geschrieben.
Die Tests sind automatisiert. Das bedeutet, dass sie im normalen Build Prozess mitausgeführt werden und dieser abgebrochen wird, falls mindestens ein Test fehlschlägt. Selbst ohne Vorwissen des Programmierers zieht ein Build nach einer potentiellen Änderung am Code somit ein Durchlaufen aller Tests nach sich, das nur im Fehlerfall "meckert". Wir verwenden in unserem Projekt das Build System Maven. Die Verwendung des Surefire-Plugins sorgt dafür, dass der Standard Bauprozess die automatische Durchführung der Tests beinhaltet.
Die Tests sind nicht wirklich gründlich, da nur für wenig Code Tests geschrieben wurden. Die Funktionalitäten, die getestet wurden, sind dafür gründlich getestet. In der Beschreibung der geschriebenen Tests und im Abschnitt Test Coverage wird näher darauf eingegangen.
Die Tests sind wiederholbar. Nach dem Durchlauf der Tests sind keine Zustände verändert worden, somit wird kein Einfluss auf zukünftige Testdurchläufe genommen. Die Tests sind außerdem unabhängig von Zeit und Zufall.
Die Tests sind unabhängig voneinander, da alle benötigten Startzustände in jedem Test erneut hergestellt und nicht von bereits gelaufenen Tests wiederverwendet werden.
Die Tests sind größtenteils simpel gehalten. Die Methoden und Dummy-Klassen bei den Tests mit Mocking verbergen ein Stück weit Komplexität. Dabei wurde jedoch versucht, sprechende Namen für Methoden und auch Konstanten zu wählen, sodass ein Verständnis des Test-Codes möglichst leicht fällt. Die Auslagerung wurde als sinnvoll erachtet, da sonst viel Code wiederholt werden müsste und ein nachträgliches Ändern der Anforderungen und damit auch der Tests wesentlich aufwändiger würde.
Da die Tests leider nur nachträglich erstellt wurden, um den Anforderungen der Prüfungsleistung zu entsprechen, ist die Test Coverage nicht sehr hoch.
Die Test Coverage der Smarthome Bridge und des Minecraft Smarthomes wurde zunächst einzeln mit dem jacoco-maven-plugin berechnet.
Zunächst die Ergebnisse für das Minecraft Smarthome:

Die Test Coverage nach Statements beträgt also 6%, die Coverage nach Verzweigungen 8%.
Da die Tests nicht gleichmäßig über die Codebasis verteilt sind, sondern stattdessen nur zwei Klassen getestet wurden, ist auch die Coverage für diese beiden Klassen von Interesse. Zum einen für SadCurrentConverter:

Und außerdem SadMinecraftCommandArgumentParser:

Sämtliche missed Instructions und Branches betreffen hier ausschließlich Fehlerbehandlung im zu testenden Code, wie das Prüfen auf unerlaubte Parameterwerte.
int to = (int) Math.round(convertInternal(from, maxFrom, maxTo));
if (to < 1 || maxTo < to) {
throw new PluginException("We have verkackt");
}Diese Verzweigung insbesondere soll nie ausgeführt werden und weist im Fall, dass sie betreten wird, nur darauf hin, dass der Code direkt davor nicht korrekt arbeitet. Sie ist sogesehen ein Laufzeit-Test und sollte daher eher in den Unit Tests sein oder zumindest ein assert- und kein if-Statement sein.
Folgendes Ergebis ergab die Code-Coverage beim Core Modul der Smarthome-Bridge.

Ebenso wie beim Minecraft Smarthome sind die Tests nicht gleichmäßig über die Codebasis verteilt.
Dies wird ersichtlich wenn ein genauerer Blick auf die Test Coverage geworfen wird.
Hier die aufgeschlüsselten Felder der Test Coverage:

Es wird ersichtlich, dass noch viele Klassen ungetestet sind. Die Core Klasse im Start Package ist jedoch schon gut von den Tests abgedeckt.

Die Coverage ist ein guter Indiz für Code-Teile die noch nicht von Tests abgedeckt werden. Um die Testabdeckung zu verbessern, sollten für die noch ungetesteten Klassen entsprechende Tests geschrieben werden. Damit kann sichergestellt werden, das ihr Verhalten (weitestgehend) korrekt ist.
Die Metrik "Missed Instructions" bezieht sich auf den Anteil der Anweisungen im Code, die nicht von Tests abgedeckt wurden. Wenn eine Anweisung nicht abgedeckt ist, bedeutet das, dass sie während der Ausführung der Tests nicht erreicht wurde. Dies kann bedeuten, dass der entsprechende Codepfad nicht überprüft wurde und mögliche Fehler oder unerwartete Verhaltensweisen unentdeckt bleiben können.
Die Metrik "Missed Branches" bezieht sich auf den Anteil der Verzweigungen im Code, die nicht von Tests abgedeckt wurden. Eine Verzweigung entsteht, wenn der Code zwischen verschiedenen Entscheidungspunkten unterschiedliche Pfade nehmen kann. Wenn eine Verzweigung nicht abgedeckt ist, bedeutet das, dass mindestens einer der möglichen Pfade nicht getestet wurde. Dadurch besteht das Risiko, dass Fehler oder unerwartetes Verhalten in den nicht abgedeckten Codepfaden auftreten.