Ich habe eine Reihe von Geschäftslogikmethoden, die Objekte und Objektlisten aus dem Cache speichern und abrufen (mit Filterung).
Erwägen
IList<TObject> AllFromCache() { ... }
TObject FetchById(guid id) { ... }
IList<TObject> FilterByPropertry(int property) { ... }
Fetch..
und Filter..
würde aufrufen, AllFromCache
was den Cache füllen und zurückgeben würde, wenn es nicht da ist, und einfach daraus zurückkehren, wenn es ist.
Ich scheue mich im Allgemeinen davor zurück, diese Geräte zu testen. Was sind die Best Practices für Unit-Tests für diese Art von Struktur?
Ich dachte darüber nach, den Cache bei TestInitialize zu füllen und bei TestCleanup zu entfernen, aber das fühlt sich für mich nicht richtig an (obwohl es gut sein könnte).
quelle
Das Prinzip der Einzelverantwortung ist hier Ihr bester Freund.
Verschieben Sie zunächst AllFromCache () in eine Repository-Klasse und nennen Sie sie GetAll (). Das Abrufen aus dem Cache ist ein Implementierungsdetail des Repositorys und sollte dem aufrufenden Code nicht bekannt sein.
Dies macht das Testen Ihrer Filterklasse angenehm und einfach. Es ist nicht länger wichtig, woher du es bekommst.
Binden Sie anschließend die Klasse, die die Daten aus der Datenbank (oder von einem beliebigen Ort) abruft, in einen Caching-Wrapper ein.
AOP ist dafür eine gute Technik. Es ist eines der wenigen Dinge, in denen es sehr gut ist.
Mit Tools wie PostSharp können Sie festlegen, dass alle mit einem ausgewählten Attribut gekennzeichneten Methoden zwischengespeichert werden. Wenn dies jedoch das einzige ist, das Sie zwischenspeichern, müssen Sie nicht so weit gehen, dass Sie über ein AOP-Framework verfügen. Stellen Sie einfach ein Repository und einen Caching-Wrapper bereit, die dieselbe Schnittstelle verwenden, und fügen Sie diese in die aufrufende Klasse ein.
z.B.
Sehen Sie, wie Sie das Repository-Implementierungswissen aus dem ProductManager entfernt haben? Sehen Sie auch, wie Sie das Prinzip der Einzelverantwortung eingehalten haben, indem Sie eine Klasse für die Datenextraktion, eine Klasse für den Datenabruf und eine Klasse für die Zwischenspeicherung eingerichtet haben?
Sie können jetzt den ProductManager mit einem dieser Repositorys instanziieren und Zwischenspeicherung durchführen ... oder nicht. Dies ist später unglaublich nützlich, wenn Sie einen verwirrenden Fehler bemerken, von dem Sie vermuten, dass er auf den Cache zurückzuführen ist.
(Wenn Sie einen IOC-Container verwenden, ist dies noch besser. Die Anpassung sollte offensichtlich sein.)
Und in Ihren ProductManager-Tests
Der Cache muss überhaupt nicht getestet werden.
Jetzt lautet die Frage: Soll ich das CachedProductRepository testen? Ich schlage nicht vor. Der Cache ist ziemlich unbestimmt. Das Framework erledigt damit Dinge, die außerhalb Ihrer Kontrolle liegen. Zum Beispiel einfach Sachen entfernen, wenn sie zu voll sind. Du wirst mit Tests enden, die einmal in einem blauen Mond scheitern und du wirst nie wirklich verstehen warum.
Und nachdem ich die oben vorgeschlagenen Änderungen vorgenommen habe, gibt es wirklich nicht so viel Logik zum Testen. Der wirklich wichtige Test, die Filtermethode, wird vorhanden und vollständig vom Detail von GetAll () abstrahiert sein. GetAll () bekommt einfach alles. Von irgendwo.
quelle
Ihr vorgeschlagener Ansatz ist das, was ich tun würde. In Anbetracht Ihrer Beschreibung sollte das Ergebnis der Methode das gleiche sein, unabhängig davon, ob sich das Objekt im Cache befindet oder nicht: Sie sollten immer noch das gleiche Ergebnis erhalten. Dies ist einfach zu testen, indem der Cache vor jedem Test auf eine bestimmte Weise eingerichtet wird. Es gibt wahrscheinlich einige zusätzliche Fälle, z. B. wenn die Guid
null
die angeforderte Eigenschaft aufweist oder kein Objekt vorhanden ist. diese können auch getestet werden.Zusätzlich Sie können prüfen , zu erwarten , dass sich das Objekt im Cache nach der Methode zurückgibt , vorhanden sein, unabhängig davon , ob sie im Cache in erster Linie war. Dies ist umstritten, da einige Leute (ich selbst eingeschlossen) argumentieren würden, dass es Ihnen wichtig ist, was Sie von Ihrer Schnittstelle zurückerhalten, und nicht, wie Sie es erhalten (dh Sie testen, ob die Schnittstelle wie erwartet funktioniert und keine spezifische Implementierung hat). Wenn Sie es für wichtig halten, haben Sie die Möglichkeit, dies zu testen.
quelle
Eigentlich ist das der einzig richtige Weg. Dafür gibt es diese beiden Funktionen: Voraussetzungen schaffen und aufräumen. Wenn die Voraussetzungen nicht erfüllt sind, funktioniert Ihr Programm möglicherweise nicht.
quelle
Ich habe in letzter Zeit an einigen Tests mit Cache gearbeitet. Ich habe einen Wrapper für die Klasse erstellt, der mit dem Cache funktioniert, und dann die Zusicherung erhalten, dass dieser Wrapper aufgerufen wird.
Ich tat dies hauptsächlich, weil die vorhandene Klasse, die mit Cache arbeitet, statisch war.
quelle
Anscheinend möchten Sie die Caching-Logik testen, aber nicht die Auffüllungslogik. Daher würde ich vorschlagen, dass Sie sich über das lustig machen, was Sie nicht testen müssen - das Auffüllen.
Ihre
AllFromCache()
Methode kümmert sich um das Auffüllen des Caches, und dieser sollte an etwas anderes delegiert werden, beispielsweise an einen Wertelieferanten. So würde Ihr Code aussehenJetzt können Sie den Lieferanten für den Test verspotten, um einige vordefinierte Werte zurückzugeben. Auf diese Weise können Sie das tatsächliche Filtern und Abrufen testen und keine Objekte laden.
quelle