Ja, SOLID ist eine sehr gute Möglichkeit, Code zu entwerfen, der einfach getestet werden kann. Als kurze Grundierung:
S - Prinzip der Einzelverantwortung: Ein Objekt sollte genau eines tun und sollte das einzige Objekt in der Codebasis sein, das das eine tut. Nehmen Sie zum Beispiel eine Domainklasse, zum Beispiel eine Rechnung. Die Invoice-Klasse sollte die Datenstruktur und Geschäftsregeln einer Rechnung darstellen, wie sie im System verwendet werden. Es sollte die einzige Klasse sein, die eine Rechnung in der Codebasis darstellt. Dies kann weiter unterteilt werden, um zu sagen, dass eine Methode einen einzigen Zweck haben und die einzige Methode in der Codebasis sein sollte, die diesen Bedarf erfüllt.
Durch Befolgen dieses Prinzips erhöhen Sie die Testbarkeit Ihres Entwurfs, indem Sie die Anzahl der Tests verringern, die Sie schreiben müssen, um die gleiche Funktionalität für verschiedene Objekte zu testen. Außerdem erhalten Sie in der Regel kleinere Teile der Funktionalität, die isoliert einfacher zu testen sind.
O - Open / Closed-Prinzip: Eine Klasse sollte offen für Erweiterungen, aber geschlossen für Änderungen sein . Sobald ein Objekt vorhanden ist und ordnungsgemäß funktioniert, sollte es im Idealfall nicht mehr erforderlich sein, zu diesem Objekt zurückzukehren, um Änderungen vorzunehmen, die neue Funktionen hinzufügen. Stattdessen sollte das Objekt erweitert werden, indem es abgeleitet wird oder indem neue oder andere Abhängigkeitsimplementierungen hinzugefügt werden, um diese neue Funktionalität bereitzustellen. Dies vermeidet eine Regression. Sie können die neue Funktionalität einführen, wann und wo sie benötigt wird, ohne das Verhalten des Objekts zu ändern, da es bereits an anderer Stelle verwendet wird.
Durch die Einhaltung dieses Prinzips erhöhen Sie im Allgemeinen die Fähigkeit des Codes, "Mocks" zu tolerieren, und Sie müssen auch keine Tests umschreiben, um neues Verhalten zu antizipieren. Alle vorhandenen Tests für ein Objekt sollten weiterhin auf der nicht erweiterten Implementierung funktionieren, während auch neue Tests für neue Funktionen unter Verwendung der erweiterten Implementierung funktionieren sollten.
L - Liskov-Substitutionsprinzip: Eine Klasse A, abhängig von Klasse B, sollte in der Lage sein, jedes X: B zu verwenden, ohne den Unterschied zu kennen. Dies bedeutet im Grunde, dass alles, was Sie als Abhängigkeit verwenden, ein ähnliches Verhalten haben sollte, wie es von der abhängigen Klasse gesehen wird. Nehmen wir als kurzes Beispiel an, Sie haben eine IWriter-Schnittstelle, die Write (Zeichenfolge) verfügbar macht, die von ConsoleWriter implementiert wird. Jetzt müssen Sie stattdessen in eine Datei schreiben, um FileWriter zu erstellen. Dabei müssen Sie sicherstellen, dass FileWriter auf dieselbe Weise wie ConsoleWriter verwendet werden kann (dh, dass die abhängige Person nur mit Write (string) interagieren kann), und dass dies möglicherweise zusätzliche Informationen erfordert, die FileWriter benötigt Job (wie der Pfad und die Datei, in die geschrieben werden soll) müssen von einer anderen als der abhängigen Stelle bereitgestellt werden.
Dies ist für das Schreiben von testbarem Code enorm wichtig, da bei einem Design, das dem LSP entspricht, ein "verspottetes" Objekt jederzeit durch das Original ersetzt werden kann, ohne das erwartete Verhalten zu ändern, sodass kleine Codeteile ohne Bedenken einzeln getestet werden können dass das System dann mit den eingesteckten realen Objekten arbeitet.
I - Prinzip der Schnittstellentrennung : Eine Schnittstelle sollte über so wenige Methoden verfügen, wie möglich, um die Funktionalität der durch die Schnittstelle definierten Rolle bereitzustellen . Einfach ausgedrückt, sind mehr kleinere Schnittstellen besser als weniger größere Schnittstellen. Dies liegt daran, dass eine große Schnittstelle mehr Änderungsgründe hat und an anderer Stelle in der Codebasis weitere Änderungen verursacht, die möglicherweise nicht erforderlich sind.
Die Einhaltung von ISP verbessert die Testbarkeit, indem die Komplexität der zu testenden Systeme und die Abhängigkeiten dieser SUTs verringert werden. Wenn das zu testende Objekt von einer Schnittstelle IDoThreeThings abhängt, die DoOne (), DoTwo () und DoThree () verfügbar macht, müssen Sie ein Objekt verspotten, das alle drei Methoden implementiert, auch wenn das Objekt nur die DoTwo-Methode verwendet. Wenn das Objekt jedoch nur von IDoTwo abhängt (wodurch nur DoTwo verfügbar gemacht wird), können Sie ein Objekt mit dieser einen Methode einfacher verspotten.
D - Abhängigkeitsinversion Prinzip: Konkretionen und Abstraktionen sollten niemals von anderen Konkretionen abhängen, sondern von Abstraktionen . Dieses Prinzip erzwingt direkt den Grundsatz der losen Kopplung. Ein Objekt sollte niemals wissen müssen, was ein Objekt IST; Es sollte sich stattdessen darum kümmern, was ein Objekt tut. Die Verwendung von Interfaces und / oder abstrakten Basisklassen ist daher bei der Definition von Eigenschaften und Parametern eines Objekts oder einer Methode immer der Verwendung konkreter Implementierungen vorzuziehen. Auf diese Weise können Sie eine Implementierung gegen eine andere austauschen, ohne die Verwendung ändern zu müssen (wenn Sie auch LSP befolgen, was mit DIP einhergeht).
Auch dies ist aus Gründen der Testbarkeit von großer Bedeutung, da Sie erneut eine Scheinimplementierung einer Abhängigkeit anstelle einer "Produktions" -Implementierung in das zu testende Objekt einfügen können, während Sie das Objekt weiterhin in der exakten Form testen, in der es sich befindet in Produktion. Dies ist der Schlüssel zum Testen von Einheiten "isoliert".