{"id":419,"date":"2015-05-24T17:41:35","date_gmt":"2015-05-24T15:41:35","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=419"},"modified":"2015-07-04T14:37:11","modified_gmt":"2015-07-04T12:37:11","slug":"von-tests-und-komponenten","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/von-tests-und-komponenten\/","title":{"rendered":"Von Tests und Komponenten"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Wer von euch liefert gerne gro\u00dfartige Qualit\u00e4t ab? Wo beginnt gro\u00dfartige Qualit\u00e4t des gesamten Systems? Was ist Voraussetzung daf\u00fcr, dass eine Anwendung &#8211; bestehend aus vielen kleinen Bausteinen &#8211; unseren eigenen sowie den Anspruch unserer Kunden an eine hohe Qualit\u00e4t erf\u00fcllt? Wie w\u00e4re es, wenn wir das gesamte System testen? Wie w\u00e4re es, wenn jede einzelne Komponente des Systems getestet ist? Wenn jeder Baustein f\u00fcr sich funktioniert, funktioniert dann auch das gesamte System?<\/p>\n<p>&nbsp;<\/p>\n<h2>Komponententests (Unit Tests)<\/h2>\n<p>Die kleinste Einheit in einem System, nennen wir sie Methode, hat eine definierte Schnittstelle mit Ein- und Ausgaben und nach Anwendung der SOLID-Prinzipien eine beherrschbare Komplexit\u00e4t. Daher ist es vielleicht eine gute Idee, hier mit den Tests zu beginnen.<\/p>\n<p>Der Test von Komponenten hat zum Ziel, einen isolierten Baustein (z.B. eine Methode) losgel\u00f6st vom Rest der Anwendung zu testen und die Korrektheit abzusichern. Dieser isolierte Baustein wird auch als SUT &#8211; &#8222;system under test&#8220; oder &#8222;subject under test&#8220; bezeichnet [2]. Erstellen wir eine neue Klasse mit einzelnen Methoden, ist es leicht die Methoden isoliert zu betrachten. Schwieriger wird es, wenn nachtr\u00e4glich Komponententests f\u00fcr ein bestehendes System hinzugef\u00fcgt werden. Dabei besteht die Herausforderung, zu testende Einheiten vom Rest des System zu isolieren und somit f\u00fcr sich zu pr\u00fcfen. Welche Methoden und Techniken hierf\u00fcr bereitstehen, werden wir uns in einem sp\u00e4teren Artikel ansehen (siehe auch [1]).<\/p>\n<p>Sobald die Isolation nicht gegeben ist und zwei oder mehr von einander abh\u00e4ngige Komponenten zusammen getestet werden, sprechen wir von <em>Integrationstests<\/em>, zu denen ein eigener Artikel erscheinen wird. Das bedeutet auch, dass jede Abh\u00e4ngigkeit zur Umwelt des SUT f\u00fcr den Komponententest zu entfernen ist. Also Zugriffe auf das Dateisystem, Netzwerke oder Datenbanken. Diese Abh\u00e4ngigkeiten sind f\u00fcr den Unit Test aufzul\u00f6sen, da wir keine direkte Kontrolle \u00fcber ein Netzwerk haben und wir auch gar nicht das Netzwerk, sondern unsere Komponente testen wollen. Doch wie kann ich eine Abh\u00e4ngigkeit zu einem Dateisystem tempor\u00e4r f\u00fcr die Ausf\u00fchrung der Tests aufl\u00f6sen?<\/p>\n<p>Daf\u00fcr gibt es ein paar n\u00fctzliche Techniken, von denen nachfolgend einige vorgestellt werden.<\/p>\n<p><strong>Fake<\/strong><\/p>\n<p>Ein Fake ist ein Objekt, welches sich in Teilen so verh\u00e4lt wie die Abh\u00e4ngigkeit zu unserem SUT, jedoch nur die Implementierungen enth\u00e4lt, welche wir f\u00fcr einen bestimmte Test ben\u00f6tigen. Dies kann sinnvoll sein, wenn die Abh\u00e4ngigkeit weitere Abh\u00e4ngigkeiten hat, schwer zu erstellen oder langsam ist.<\/p>\n<p><strong>Stub<\/strong><\/p>\n<p>Ein Stub ist ein Objekt, welches die Abh\u00e4ngigkeit ersetzt und unserem SUT genau die f\u00fcr einen bestimmten Test festgelegten Daten zur\u00fcck gibt, unabh\u00e4ngig von den Eingabeparametern. Dies ist eine sehr einfache Methode, Abh\u00e4ngigkeiten f\u00fcr einen Test loszuwerden.<\/p>\n<p><strong>Mock<\/strong><\/p>\n<p>Ein Mock ist ein Objekt, welches die Abh\u00e4ngigkeit ersetzt und unserem SUT je nach Eingangsparameter die von uns festgelegten Daten zur\u00fcck gibt. Mocks werden h\u00e4ufig mit Hilfe von entsprechenden Frameworks verwendet, die es deutlich vereinfachen, Mock-Objekte zu parametrieren und zu erzeugen.<\/p>\n<p>Die Definition von Stub und Mock unterscheiden sich je nach Quelle. Die oben genannten Definition entstammt der Wikipedia [3] und entspricht der \u00fcblichen Verwendung von Mock-Frameworks [siehe auch [4]). Roy Osherove schreibt in [2], dass das Mock-Objekt selbst nach Aufruf des SUT gepr\u00fcft wird, da dies alle Aufrufe aufzeichnet und somit den Test validiert. Wir verwenden in den meisten F\u00e4llen die erste Definition, wenn wir mit Mock-Objekten arbeiten.<\/p>\n<p>Wie bereits angesprochen, k\u00f6nnen diese Techniken deutlich leichter eingesetzt werden, wenn die Kopplung unseres SUT zu seinen Abh\u00e4ngigkeiten gering ist und SOLID angewendet wurde. So kann beispielsweise eine Fabrikklasse ein spezielles Objekt f\u00fcr den Test bereitstellen, wenn das <a href=\"http:\/\/invidit.de\/blog\/dependency-inversion-principle\/\">DIP<\/a> angewendet wurde.<\/p>\n<p>Der grunds\u00e4tzliche Aufbau eines Unit Tests ist immer gleich:<\/p>\n<ol>\n<li>Initialisieren des Ausgangszustandes; es werden alle f\u00fcr den konkreten Test erforderlichen Bausteine erzeugt<\/li>\n<li>Aufruf der zu testenden Methode<\/li>\n<li>Pr\u00fcfung des Ergebnisses; hier wird mit Zusicherungen (assertions) gearbeitet, die das tats\u00e4chliche gegen das gew\u00fcnschte Verhalten pr\u00fcfen<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Eine besondere Praktik\u00a0 im Zusammenhang mit Komponententests ist das Test-Driven-Development (TTD), bei dem der Test-First-Ansatz verfolgt wird. Dabei wird zuerst der Komponententest erstellt und anschlie\u00dfend der Produktionscode implementiert. Diese Vorgehensweise ist nicht zwingend erforderlich f\u00fcr das Schreiben von Komponententests, hat jedoch einige Vorteile, die wir in einem weiteren Artikel noch beleuchten werden.<\/p>\n<p>&nbsp;<\/p>\n<p>Die nachfolgenden Kriterien werden von guten Unit Tests erf\u00fcllt:<\/p>\n<ul>\n<li>ein Komponententest sollte automatisiert und wiederholbar sein (siehe auch <a href=\"http:\/\/invidit.de\/blog\/testautomatisierung\/\">Testautomatisierung<\/a>)<\/li>\n<li>er sollte einfach zu implementieren sein<\/li>\n<li>jeder sollte in der Lage sein, den Test auszuf\u00fchren<\/li>\n<li>er sollte auf Knopfdruck oder regelm\u00e4\u00dfig ablaufen<\/li>\n<li>er sollte schnell laufen<\/li>\n<\/ul>\n<p>In einigen Punkten unterst\u00fctzen verschiedene Testframeworks die Arbeit, wie z. B. xUnit (also JUnit f\u00fcr Java). Konkrete Werkzeuge schauen wir uns im einem sp\u00e4teren Artikel genauer an. Die Notwendigkeit f\u00fcr ein schnelles Ausf\u00fchren der Tests wird dann deutlich, wenn mit der Zeit einige hundert oder sogar tausend Tests existieren. Wenn jeder dieser Tests auch nur 10 Sekunden l\u00e4uft, ist dies bei 1000 Tests eine Gesamtlaufzelt von 2,77 Stunden. Dabei w\u00fcrde das schnelle Feedback an den Entwickler, ob das erwartete Verhalten der Komponente mit dem tats\u00e4chlichen Verhalten \u00fcbereinstimmt, uninteressant f\u00fcr kleine Programmierzyklen werden.<\/p>\n<p>&nbsp;<\/p>\n<h2>Unsere Erfahrung mit Komponententests<\/h2>\n<p>H\u00e4ufig beginnen wir das Programmieren mit der Idee &#8211; &#8222;Lass uns mal einen Test schreiben.&#8220;. Wir haben dabei die Erfahrung gemacht, dass wir fokussierter Arbeiten, da wir genau ein bestimmtes Verhalten der gew\u00fcnschten Komponente betrachten und daf\u00fcr den Test schreiben. Das vereinfacht die Arbeit deutlich. Bei sp\u00e4teren \u00c4nderungen am\u00a0 &#8211; durch Tests gesicherten &#8211; Code ist es ein gutes Gef\u00fchl von Sicherheit, anhand der automatisierten Komponententests direktes Feedback \u00fcber unsere \u00c4nderungen zu bekommen: funktioniert die \u00c4nderung wie gew\u00fcnscht? Welche Nebeneffekte hat die \u00c4nderung auf andere Teile der Anwendung? Uns f\u00e4llt es einfacher, Tests direkt zu beginn schreiben wenn wir eine neue Klasse oder Methode entwickeln. Tests nachtr\u00e4glich in legacy code einzuf\u00fchren birgt immer die Gefahr, dass der Produktionscode erst angepasst werden muss (SOLID), damit Tests \u00fcberhaupt m\u00f6glich sind.<\/p>\n<p>Weiterhin sind es immer wieder kleine Erfolgserlebnisse eine Anwendung inkrementell wachsen zu sehen und dabei in kurzer Zeit gr\u00fcn auf wei\u00df best\u00e4tigt zu bekommen, dass alle einzelnen Komponenten f\u00fcr sich funktionieren. Wenn jetzt noch alle richtig zusammengesteckt werden, steht einer hoch qualitativen Anwendung nichts mehr im Wege (Annahme: die Tests sichern das ben\u00f6tigte Verhalten des Kunden :-)).<\/p>\n<p>Wie viel Testabdeckung ist w\u00fcnschenswert, sinnvoll oder notwendig? Das Ziel von Komponententests ist bei nachtr\u00e4glicher \u00c4nderung des Codes eine Sicherheit zu haben, dass die \u00c4nderung frei von unerw\u00fcnschten Nebeneffekten ist und somit kein anderes Verhalten des Systems negativ beeinflusst. Komponententests sind da einzusetzen, wo sie zu diesem Ziel einen sinnvollen Beitrag leisten.<\/p>\n<p>&nbsp;<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Jeder\u00a0 von uns kann ein noch so gro\u00dfes Problem meistern und eine L\u00f6sung finden. Die Herausforderung dabei ist, das Problem in kleine, noch kleinere und kleinste Einheiten zu zerlegen, welche dann wieder beherrschbar werden. Wird nach und nach in kleinen Schritten jedes dieser kleinsten Problem gel\u00f6st, entsteht dadurch die L\u00f6sung des Gesamtproblems. Oder anders ausgedr\u00fcckt &#8211; die l\u00e4ngste Reise beginnt mit dem ersten Schritt.<\/p>\n<p>&nbsp;<\/p>\n<p>Viel Spa\u00df beim Anwenden.<\/p>\n<p>Eure Spa\u00df-Coder.<\/p>\n<p>&nbsp;<\/p>\n<p>[1] Feathers, Michael C. <i>Effektives Arbeiten mit Legacy Code: Refactoring und Testen bestehender Software<\/i>. mitp Verlags GmbH &amp; Co. KG, 2011.<\/p>\n<p>[2] Osherove, Roy. <i>The art of unit testing<\/i>. mitp, 2010.<\/p>\n<p>[3] https:\/\/de.wikipedia.org\/wiki\/Modultest, aufgerufen am 24.05.2015<\/p>\n<div class=\"gs_citr\" tabindex=\"0\">[4] https:\/\/de.wikipedia.org\/wiki\/Mocking_Framework, aufgerufen am 24.05.2015<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Wer von euch liefert gerne gro\u00dfartige Qualit\u00e4t ab? Wo beginnt gro\u00dfartige Qualit\u00e4t des gesamten Systems? Was ist Voraussetzung daf\u00fcr, dass eine Anwendung &#8211; bestehend aus vielen kleinen Bausteinen &#8211; unseren eigenen sowie den Anspruch unserer Kunden an eine hohe Qualit\u00e4t erf\u00fcllt? Wie w\u00e4re es, wenn wir das gesamte System testen? Wie w\u00e4re es, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":508,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[80,78,79,69,77,75],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/419"}],"collection":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=419"}],"version-history":[{"count":20,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/419\/revisions"}],"predecessor-version":[{"id":784,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/419\/revisions\/784"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/508"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=419"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=419"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=419"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}