Skip to main content
swissICT Booster  |  M&F Academy  |  M&F Events   |  +41 44 747 44 44  | 
3 Minuten Lesezeit (606 Worte)

Unit Tests, Network Stream und DLL

Dieser Blog-Beitrag baut auf diesem Unit Testing-Blog auf, welcher aufgrund eines internen Workshops mit unserem Software-Testing Experten, Roger Billeter, entstanden ist. Dabei fokussieren wir uns auf den Umgang mit Abhängigkeiten von Software-Units. Unteranderem werden folgende Fragen beantwortet:

  • Wie kann zum Beispiel eine Einheit, welche eine TCP-Verbindung verwendet, getestet werden?
  • Wie kann generell getestet werden, wenn eine Abhängigkeit zu einer DLL besteht?

 

Kurzzusammenfassung des Unit Testing-Blogs

  • Definition Unit Test: Modultest, der ein funktionales Einzelteil auf korrekte Funktionalität prüft.
  • Ein Unit Test soll unabhängig, schnell durchlaufen und einfach verständlich sein. Das heisst er ist entsprechend klein und spezialisiert.
  • Das Resultat eines Unit Tests ist: FAIL oder PASS.
  • Auflösen von Abhängigkeiten, in dem Mocks als Platzhalter für reale Objekte eingesetzt werden:
    Device under Test
    Abb. 1: abgekoppeltes "Device under Test" (DUT)

    Dazu werden Interfaces der entsprechenden Klassen sowie Dependency Injection eingesetzt.

 

Network Stream: unbezwingbare Abhängigkeiten auflösen

Ausgangslage:

  • Eine Software verwendet Interfaces, welche während Unit Tests gemockt werden können.
  • Im Beispiel steht ein Interface "IServerAccess" zur Verfügung, welches erlaubt, dass Softwaremodule, welche ServerAccess verwenden, unabhängig von ServerAccess getestet werden können. Das Mocking-Framework stellt dafür ein Stub-Objekt zur Verfügung, welches IServerAccess implementiert.
  • Jedoch kann das Modul "ServerAccess" selber in dieser Konstellation nicht unabhängig von TcpClient und NetworkStream getestet werden. Es gibt keine Interfaces dieser zwei Klassen, welche gemockt werden könnten.
    Server Access
    Abb. 2: Zu testendes Modul "ServerAccess" hat für den Test unerwünschte/unbrauchbare Abhängigkeiten.

 

Problem 1: Um das Modul "ServerAccess" nun vom TCPClient und vom NetworkStream abkoppeln zu können, müsste ein entsprechendes Interfaces (ITCPClient) verfügbar sein. Das bedeutet implizit, dass TCPClient auch nicht mehr innerhalb von "ServerAccess" instantiiert werden darf, sondern per Dependency Injection übergeben werden muss.

Problem 2: TCPClient gibt einen Networkstream zurück, mit welchem gearbeitet werden muss.

Lösungsansatz zu Problem 1: TCPClient

  • Da kein Interface existiert, muss eines erstellt werden.
  • TCPClient wird durch einen Adapter instantiiert.
  • Der Adapter verfügt über ein Interface (nur benötigte Methoden).

Lösungsansatz zum Problem 2: NetworkStream

  • Auch hier könnte ein Adapter mit Interface zum Einsatz kommen.
  • Jedoch kann hier auch eine "TestStream"-Klasse zum Einsatz kommen, welche von "NetworkStream" ableitet und nur die Methoden Write() und Read() überschreibt, um einen Datenstrom zu simulieren. Diese Testklasse kann zusätzliche Funktionalität beherbergen, um beispielsweise auch ein Timeout zu simulieren.

    Abb. 3: TCPClient mit Adapter

    Unit Test Struktur
    Abb. 4: Testkonstellation für den Unit Test von "ServerAccess"


DLL: Kapselung einer Bibliothek

Analog dem Lösungsansatz von "ServerAccess" kann auch eine DLL gehandhabt werden. Das nachfolgende UML zeigt eine .dll, welche durch einen Wrapper gekapselt wird. Da für den Wrapper ein Interface zur Verfügung steht, kann die übergeordnete Klasse "SafetyTester" auch hier unabhängig von der DLL getestet werden, da deren Wrapper durch das Mocking-Framework abgekapselt werden kann.

DLL Abhängigkeit 
Abb. 5: "SafetyTester" mit Wrapper (links) und als Device under Test (DUT, rechts)

Der Wrapper und der Test wiederum müssen nicht die gesamte Funktionalität der DLL abdecken, sondern nur jene Teile, welche für den Betrieb des "Device under Test" (DUT) benötigt werden.

 

Fazit

  • Suche nach Interfaces, welche das Testframework mocken kann. Wenn es keine gibt, dann schaffe welche. Mit Wissen und Kreativität lassen sich, durch den gezielten Einsatz von Design Patterns, elegante Lösungen finden. Und schon wird das Modul testbar.
  • Beispielsweise könnte die Dependency Injection, welche im Beispiel oben für den Test des ServerAccess umgesetzt wurde, später bei Bedarf mit einem Factory Pattern ergänzt werden, um Abhängigkeiten flexibel laden zu können.
  • Mit jedem erstellten Test steigt die Erfahrung und es fällt einem viel leichter, entsprechende Unit Tests von Beginn weg zu schreiben. Auch wenn kein "Test Driven Development" an sich eingesetzt wird, bewirken solche Unit Tests (gerade auch durch einen gezielten Einsatz von Design Patterns) ein besseres Design.
  • Code-Reviews bringen kreative Inputs von anderen Entwicklern. Und diese Mittel sollte möglichst früh genutzt werden.
0
Data Science in der Industrie 4.0
Effiziente Umsetzung von Industrie 4.0-Lösungen da...

Ähnliche Beiträge

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Mittwoch, 15. Mai 2024

Sicherheitscode (Captcha)