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.
public class ProductManager
{
private IProductRepository ProductRepository { get; set; }
public ProductManager
{
ProductRepository = productRepository;
}
Product FetchById(guid id) { ... }
IList<Product> FilterByPropertry(int property) { ... }
}
public interface IProductRepository
{
IList<Product> GetAll();
}
public class SqlProductRepository : IProductRepository
{
public IList<Product> GetAll()
{
// DB Connection, fetch
}
}
public class CachedProductRepository : IProductRepository
{
private IProductRepository ProductRepository { get; set; }
public CachedProductRepository (IProductRepository productRepository)
{
ProductRepository = productRepository;
}
public IList<Product> GetAll()
{
// Check cache, if exists then return,
// if not then call GetAll() on inner repository
}
}
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.
productManager = new ProductManager(
new SqlProductRepository()
);
productManager = new ProductManager(
new CachedProductRepository(new SqlProductRepository())
);
(Wenn Sie einen IOC-Container verwenden, ist dies noch besser. Die Anpassung sollte offensichtlich sein.)
Und in Ihren ProductManager-Tests
IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();
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.