Interfaces für bessere Unittests

Alisa Schulz

06/27/2023

In diesem Blogeintrag möchte ich dir anhand eines einfachen Beispiels zeigen, wie Du Interfaces in C# gezielt einsetzten kannst, um das unabhängige Testen einzelner Klassen in deinen C#-Projekten zu ermöglichen. Das hierfür verwendete Beispiel baut auf meinem letzten Beitrag „Einstieg ins Unit-Testen in C# – Setup, Basics und Hilfreiche Tools“ auf.

Warum sollten Unittests unabhängig von der Klasse ausgeführt werden?

Der Sinn von Unittests ist es einzelne Codeabschnitte unabhängig voneinander auf ihre Funktionalität zu prüfen. Es kann jedoch vorkommen, dass Klassen oder Methoden von einer anderen Klasse abhängen. In diesen Fällen lassen sich die Codeabschnitte nicht unabhängig voneinander Testen. Besonders problematisch ist dies, wenn die Klasse, von der unser zu testender Abschnitt abhängt, noch nicht vollständig getestet ist, fehlerhaft ist oder vielleicht noch gar nicht implementiert wurde.

Im folgenden Beispiel wird gezeigt, wie das NuGet Package „FakeItEasy“ und Interfaces genutzt werden können, um Klasse dennoch unabhängig testen zu können.

 

Beispiel – Die zu testende Klasse ist abhängig von einer anderen Klasse

In diesem Beispiel welchen aus dem Blogeintrag: „Einstieg ins Unit-Testen in C# – Setup, Basics und Hilfreiche Tools“ aufbaut, haben wir unsere zu testende Klasse Calculator (Abbildung 1). Wir möchten die Methode Calc(int n1, int n2, operation operation) für den Fall einer Addition testen. Calc(int n1, int n2, operation operation) ruft eine Methode einer Instance der Klasse Addition (Abbildung 2) auf welche fehlerhaft implementiert ist, aufgrund dessen gibt unsere Methode Calc(int n1, int n2, operation operation) einen Falschen Wert zurückgibt.

Der Unittest aus Abbildung 3 welcher die Methode Calc(int n1, int n2, operation operation) testet hängt zusätzlich von der Klasse Addition ab und nicht ausschließlich von unserer Klasse Calculator.


Abbildung 1 Die zu testende Klasse Calculator besitzt ein Objekt der Klasse Addition und verwendet dieses in der Methode Calc zum Berechnen des Ergebnisses


Abbildung 2 Die Klasse Addition, von welcher unsere Klasse Calculator abhängt, ist fehlerhaft implementiert und gibt als Rückgabewert für jeden Aufruf der Methode Add(int n1, int n2) die Zahl 0 zurück


Abbildung 3 Der Unittests zur Testung der Methode Calc(int n1, int n2, operation operation) wird nicht bestanden, weil die Klasse Addition das falsche Ergebnis zurückgibt

Der Test aus Abbildung 3 wird fehlschlagen, wenn wir ihn ausführen – jedoch nicht weil unsere Methode Calc(int n1, int n2, operation operation) falsch implementiert ist, sondern weil der Fehler in der Klasse Addition vorliegt. Unser Unittests kann nicht bestanden werden, solange die Klasse Addition nicht korrekt implementiert ist.

 

Beispiel – Einsetzten eines Interfaces, um unabhängiges Testen zu ermöglichen

Um nun unsere Methode Calc(int n1, int n2, operation operation) dennoch unabhängig testen zu können, erstellen wir ein Interface IAddition (Abbildung 4). Dieses soll die Schnittstelle zwischen Calculator und Addition festlegen.


Abbildung 4 Interface um die Schnittstelle zur Klasse Addition zu definieren

Wie in Abbildung 5 gezeigt erbt die Klasse Addition von dem Interface IAddition. Aufgrund dessen ist die Klasse Addition verpflichtet eine Methode Add(int n1, int n2) mit einem Float Rückgabewert zu implementieren.


Abbildung 5 Unsere Klasse Addition erbt nun vom Interface IAddition

In der Klasse Calculator ändern wir nun den Typ der Variable Addition zu IAddition. Wir können sie immer noch mit einem Objekt vom Typ Addition initialisieren, da die Klasse Addition vom Interface IAddition erbt.


Abbildung 6 Den Typ der Variable Addition in unserem Calculator ändern

 

Was bedeutet dies nun für unseren Test? Wir können nun mithilfe vom NuGet Package FakeItEasy unser Interface „mocken“, sprich ein Objekt anlegen, welches diese Schnittstelle implementiert. In Abbildung 7 im Arrange-Block in Zeile 27 wird eine Mock-Instance fakeAddition erstellt, welche das Interface IAddition implementiert. Die Variable Addition von unserem Calculator wird dann in Zeile 28 mit fakeAddition ersetzt. Außerdem legen wir in Zeile 30 fest, dass, falls von unserem fakeAddition die Methode Add mit den Eingabeparametern number1 und number2 aufgerufen wird, der Wert (number1 + number2) zurückgegeben werden soll.


Abbildung 7 Anpassung des Unittests, um eine Mock-Objekt vom Typ IAddition zu erstellen und dieses anstelle der Klasse Addition zu nutzen

Dieser Unittest wird nun erfolgreich durchlaufen, wenn er ausgeführt wird, da dieser nicht mehr auf die Klasse Addition zugreift, sondern stattdessen auf unser Mock-Objekt fakeAddition. Unsere Klasse Calculator kann nun erfolgreich und unabhängig von der Klasse Addition getestet werden.

 

Beispiele – Was noch mit FakeItEasy gemockt werden kann

FakeItEasy bietet viele Möglichkeiten, um Tests ausführlicher und umfangreicher zu gestalten. Durch Interfaces und FakeItEasy können wir Mock-Instanz erstellen und festlegen, wie diese sich verhalten soll. Im Folgenden sind ein paar Beispiele aufgelisted, was für Mock-Instanzen festgelegt werden kann.

  • Wir können festlegen, wie oft eine Methode eines Interfaces aufgerufen werden darf, damit der Test bestanden werden kann
    A.CallTo(() => fakeAddition.Add(A.Ignored, A.Ignored)).MustHaveHappened(1, Times.Exactly);
  • Wir können Rückgabeparameter einer Methode für bestimmte Eingabeparameter festlegen
    A.CallTo(() => fakeAddition.Add(number1, number2)).Returns(number1 + number2
  • Wir können Rückgabeparameter einer Methode für beliebige Eingabeparameter festlegen
    A.CallTo(() => fakeAddition.Add(A.Ignored, A.Ignored)).Returns(3);
  • Wir können eine Abfolge von Rückgabe Parameter für eine Methode festlegen, welche nacheinander zurückgegeben werden sollen unabhängig davon was die Eingabeparameter sind
    A.CallTo(() => fakeAddition.Add(A.Ignored, A.Ignored)).ReturnsNextFromSequence(new float[] { 0.1f, 0.4f, 0.0f });
  • Wir können Exceptions festlegen, die geworfen werden sollen, wenn eine Methode unserer Mock-Instanz aufgerufen wird
    A.CallTo(() => fakeAddition.Add(A.Ignored, A.Ignored)).Throws();

Weiter Möglichkeiten FakeItEasy zu verwenden und Beispiele finden Sie unter: https://fakeiteasy.github.io/docs/7.3.1/

Fazit

Wir können Interfaces gezielt einsetzten, um Schnittstellen zwischen Klassen zu definieren. Diese Interfaces gewährleisten die unabhängige Testbarkeit dieser Klassen. Mittels FakeItEasy können die Interfaces genutzt werden, um den Informationsfluss über die Schnittstelle während Unittests zu kontrollieren. Dadurch können wir beispielsweise Rückgabeparameter von bestimmten Funktionsaufrufen festlegen, kontrollieren wie oft die Funktionen eines Interfaces aufgerufen werden, darf und vieles mehr.


Geschrieben von Alisa Schulz


Weitere Beiträge

  • 19/03/2025
  • Allgemein, Unternehmen, Veranstaltungen

Wir freuen uns, euch zu einem exklusiven VDI-Event des Netzwerk Systems Engineering einzuladen, das bei uns im Büro stattfindet!Am Freitag, den 28. März 2025, wird sich alles um die ...

Weiterlesen
  • 13/03/2025
  • Allgemein, Unternehmen

MEDtech Ingenieur verstärkt sein Führungsteam: Seit März 2025 übernimmt Daniel Saffer die Rolle des Chief Technical Officers (CTO). Mit seiner langjährigen Erfahrung in der Entwicklung sicherheitskritischer Embedded-Softwarelösungen für die ...

Weiterlesen
  • 29/01/2025
  • Allgemein, Hardware, Testen

EinleitungUm bei der EMV Prüfung für die Zulassung neuer medizintechnischer Geräte das Risiko zu reduzieren machen wir gerne Vortests mit den Geräten in der Prototypen Phase. Aktuell unterstützen wir ...

Weiterlesen
Cookie-Übersicht

Die Internetseiten der MEDtech Ingenieur GmbH verwenden Cookies. Cookies sind Textdateien, welche über einen Internetbrowser auf einem Computersystem abgelegt und gespeichert werden.

Zahlreiche Internetseiten und Server verwenden Cookies. Viele Cookies enthalten eine sogenannte Cookie-ID. Eine Cookie-ID ist eine eindeutige Kennung des Cookies. Sie besteht aus einer Zeichenfolge, durch welche Internetseiten und Server dem konkreten Internetbrowser zugeordnet werden können, in dem das Cookie gespeichert wurde. Dies ermöglicht es den besuchten Internetseiten und Servern, den individuellen Browser der betroffenen Person von anderen Internetbrowsern, die andere Cookies enthalten, zu unterscheiden. Ein bestimmter Internetbrowser kann über die eindeutige Cookie-ID wiedererkannt und identifiziert werden.

Durch den Einsatz von Cookies kann die MEDtech Ingenieur GmbH den Nutzern dieser Internetseite nutzerfreundlichere Services bereitstellen, die ohne die Cookie-Setzung nicht möglich wären.

Mittels eines Cookies können die Informationen und Angebote auf unserer Internetseite im Sinne des Benutzers optimiert werden. Cookies ermöglichen uns, wie bereits erwähnt, die Benutzer unserer Internetseite wiederzuerkennen. Zweck dieser Wiedererkennung ist es, den Nutzern die Verwendung unserer Internetseite zu erleichtern. Der Benutzer einer Internetseite, die Cookies verwendet, muss beispielsweise nicht bei jedem Besuch der Internetseite erneut seine Zugangsdaten eingeben, weil dies von der Internetseite und dem auf dem Computersystem des Benutzers abgelegten Cookie übernommen wird.

Die betroffene Person kann die Setzung von Cookies durch unsere Internetseite jederzeit mittels einer entsprechenden Einstellung des genutzten Internetbrowsers verhindern und damit der Setzung von Cookies dauerhaft widersprechen. Ferner können bereits gesetzte Cookies jederzeit über einen Internetbrowser oder andere Softwareprogramme gelöscht werden. Dies ist in allen gängigen Internetbrowsern möglich. Deaktiviert die betroffene Person die Setzung von Cookies in dem genutzten Internetbrowser, sind unter Umständen nicht alle Funktionen unserer Internetseite vollumfänglich nutzbar.

Weitere Informationen erhalten Sie in unserer Datenschutzerklärung.

Unbedingt notwendige Cookies

Dieses Cookie wird benötigt, um Ihre Cookie-Einstellungen zu merken und weitere Hauptfunktionen zur Verfügung zu stellen

Um Ihnen eine Auskunft über Ihre gespeicherten personenbezogenen Daten hier (https://medtech-ingenieur.de/gespeicherte-daten-2/) geben zu können, benötigen wir einen Cookie, um Sie bei der Datenabfrage identifizieren zu können. Dieser Cookie muss aus Sicherheitsgründen deshalb aktiviert sein. Ein weiterer Cookie wird gesetzt, um diesen Banner nicht erneut anzeigen zu müssen.

Cookie-Name Beschreibung
PHPSESSID Name: PHP session
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Wir benötigt, um Sie bei der Anfrage von personenbezogenen Daten identifizieren zu können. Das Cookie wird nur gesetzt, wenn Sie eine Anfrage hier (https://medtech-ingenieur.de/gespeicherte-daten-2/) stellen.
Laufzeit: Sitzungsende
Kategorie: Unbedingt notwendige Cookies
moove_gdpr_popup Name: Cookie-Box Einstellungen
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Wird benötigt, um Ihre Cookie-Einstellungen zu speichern, um den Cookie-Banner nicht erneut anzeigen zu müssen.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
comment_author_9c90e388e3e1be4a6c594fa6ac8a3eec
comment_author_email_9c90e388e3e1be4a6c594fa6ac8a3eec
comment_author_url_9c90e388e3e1be4a6c594fa6ac8a3eec
Name: Kommentar Einstellungen
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Cookie wird angelegt, wenn Sie ein Kommentar auf MEDtech Ingenieur veröffentlichen wollen, um Sie als Autor identifizieren und den aktuellen Status Ihres Kommentars anzeigen zu können. Das Cookie enthält den angegebenen Namen. Das Cookie wird erst gesetzt, wenn Sie der Speicherung Ihrer personenbezogenen Daten zustimmen.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
rmp-rate Name: RMP Rate
Anbieter: Eigentümer der Webseite (MEDtech Ingenieur)
Zweck: Cookie wird angelegt, wenn Sie eine Bewertung eines Blogbeitrags mithilfe des Sternebewertungssystems abgeben. Ihnen wird eine anonymisierte ID zugewiesen, um zu erkennen, ob Sie einen Artikel bereits bewertet haben oder nicht. Das Cookie wird nur verwendet, um zu verhindern, dass mehrfache Bewertung abgegeben werden und erst gesetzt, wenn Sie auf einen Stern klicken.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
medtech-download-page Name: Download Page
Anbieter: Eigentümer der Webseite (MEDtech Ingenieur)
Zweck: Cookie wird angelegt, wenn Sie den Landing-Page Prozess erfolgreich durchlaufen haben. Dies geschieht nur, wenn Sie einen Content-Download von unserer Website anstreben.
Laufzeit: 1/2 Jahr
Kategorie: Unbedingt notwendige Cookies