Warum der Einsatz von Unittests generell Sinn ergibt, habe ich in vor einigen Tagen in meinem Blogeintrag Vom Saulus zum Paulus beschrieben. Doch was ist überhaupt ein Unittest? Wie grenzt er sich von anderen Arten von Tests ab?
Häufig werden insbesondere Unit- und Integrationtests vermischt – doch um überhaupt eine Trennung erreichen zu können, ist zunächst eine Klärung des Begriffs Unit nötig, ist sie schließlich das individuelle Merkmal eines Unittests. Es gilt also, zwei Fragen zu beantworten:
- Wie wird der Begriff Unit definiert?
- Welcher Unterschied besteht zwischen Unit- und Integrationtests?
Einen ersten Anhaltspunkt liefert die deutschsprachige Wikipedia auf ihrer Seite zum Thema Unittest, werden Unittests dort nämlich als
Modultest[s] (auch Komponententest[s])
bezeichnet. Ein Unittest testet also eine Komponente. Dieser Begriff hat nicht zuletzt dank der zunehmenden Verbreitung des Entwurfsmusters Inversion of Control und entsprechenden Dependency Injection-Containern wie beispielsweise LightCore eine gewisse Verbreitung erfahren.
Eine Komponente wird in diesem Zusammenhang in der Regel als ein Softwarepart definiert, der folgende Anforderungen erfüllt:
- Unabhängig: Eine Komponente ist unabhängig von einem konkreten Projekt, sondern kann flexibel in verschiedenen Projekten genutzt und nach Bedarf hinzugefügt werden. Komponenten dürfen dabei natürlich Abhängigkeiten auf andere Komponenten aufweisen, wobei diese jedoch die gleichen Anforderungen erfüllen müssen.
- Kontrakt: Eine Komponente verfügt über einen wohldefinierten Kontrakt, der neben der Syntax auch eine gewisse Semantik vorgibt. Der Verwender einer Komponente kennt nur deren Kontrakt, weshalb Komponenten gegen andere Komponenten austauschbar sind, sofern von beiden der gleiche Kontrakt erfüllt wird.
- Blackbox: Eine Komponente fungiert als Blackbox, das heißt, die Interna – auf welche Art ist welche Funktionalität implementiert – spielen für den Verwender keine Rolle. Da der Verwender eine Komponente nur auf Basis ihres Kontrakts verwendet, interessieren ihn diese Interna in der Regel auch nicht.
Unittests sind zunächst lediglich ein weiterer Verwender von Komponenten. Das heißt, dass die Unittests die an eine Komponente gestellten Anforderungen respektieren und keinen Weg suchen, diese zu umgehen.
Insbesondere bedeutet das, dass ein Unittest nur den Teil einer Komponente testet, der über ihren Kontrakt öffentlich erreichbar ist. Konkrete Implementierung ist gemäß dem Blackbox-Prinzip von Komponenten nicht nach außen – also auch nicht für Unittests – sichtbar.
Außerdem muss ein Unittest eine Komponente unabhängig von anderen Komponenten testen – ansonsten könnten Fehler nicht nur von der Komponente an sich, sondern auch von der Interaktion zwischen verschiedenen Komponenten verursacht werden. Stubs und Mocks sind aus diesem Grund essenziell für die Entwicklung von Unittests.
Zusammengefasst garantiert ein Unittest also, dass eine Komponente ihrem Kontrakt in syntaktischer und semantischer Hinsicht entspricht, Ein Integrationtest hingegen testet das Zusammenspiel verschiedener Komponenten, wobei implizit von der Korrektheit der einzelnen Komponenten ausgegangen wird.
Wann immer also eine Komponente nicht isoliert, sondern im Verbund mit anderen Komponenten getestet wird, handelt es sich bei einem Test um einen Integration- und nicht mehr um einen Unittest. Daraus ergibt sich, dass sich Unit- und Integrationtests ergänzen, aber sauber getrennt werden sollten – schließlich handelt es sich auch um verschiedene Arten von Tests.