Le principe de responsabilité unique est votre meilleur ami ici.
Tout d'abord, déplacez AllFromCache () dans une classe de référentiel et appelez-la GetAll (). Qu'il récupère du cache est un détail d'implémentation du référentiel et ne devrait pas être connu par le code appelant.
Cela rend le test de votre classe de filtrage agréable et facile. Il ne se soucie plus d'où vous l'obtenez.
Ensuite, encapsulez la classe qui obtient les données de la base de données (ou n'importe où) dans un wrapper de mise en cache.
L'AOP est une bonne technique pour cela. C'est l'une des rares choses dans lesquelles il est très bon.
À l'aide d'outils comme PostSharp , vous pouvez le configurer de sorte que toute méthode marquée avec un attribut choisi soit mise en cache. Cependant, si c'est la seule chose que vous mettez en cache, vous n'avez pas besoin d'aller aussi loin que d'avoir un framework AOP. Il suffit d'avoir un référentiel et un wrapper de mise en cache qui utilisent la même interface et l'injectent dans la classe appelante.
par exemple.
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
}
}
Vous voyez comment vous avez supprimé les connaissances d'implémentation du référentiel du ProductManager? Voyez également comment vous avez adhéré au principe de responsabilité unique en ayant une classe qui gère l'extraction des données, une classe qui gère la récupération des données et une classe qui gère la mise en cache?
Vous pouvez maintenant instancier le ProductManager avec l'un de ces référentiels et obtenir la mise en cache ... ou non. Cela est incroyablement utile plus tard lorsque vous obtenez un bug déroutant que vous soupçonnez être le résultat du cache.
productManager = new ProductManager(
new SqlProductRepository()
);
productManager = new ProductManager(
new CachedProductRepository(new SqlProductRepository())
);
(Si vous utilisez un conteneur IOC, c'est encore mieux. Il devrait être évident de savoir comment vous adapter.)
Et, dans vos tests ProductManager
IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();
Pas besoin de tester le cache du tout.
Maintenant, la question devient: Dois-je tester ce CachedProductRepository? Je suggère que non. Le cache est assez indéterminé. Le framework fait des choses hors de votre contrôle. Par exemple, en supprimant simplement des éléments lorsqu'ils sont trop pleins, par exemple. Vous allez vous retrouver avec des tests qui échouent une fois dans une lune bleue et vous ne comprendrez jamais vraiment pourquoi.
Et, après avoir apporté les modifications que j'ai suggérées ci-dessus, il n'y a vraiment pas beaucoup de logique à tester là-dedans. Le test vraiment important, la méthode de filtrage, sera là et complètement abstrait du détail de GetAll (). GetAll () juste ... obtient tout. De quelque part.