Принцип единой ответственности - ваш лучший друг здесь.
Прежде всего, переместите AllFromCache () в класс репозитория и назовите его GetAll (). То, что он извлекает из кэша, является подробностью реализации хранилища и не должно быть известно вызывающему коду.
Это делает тестирование вашего класса фильтрации приятным и легким. Больше не волнует, откуда ты это взял.
Во-вторых, оберните класс, который получает данные из базы данных (или где-либо еще) в оболочку кэширования.
АОП является хорошей техникой для этого. Это одна из немногих вещей, в которых она очень хороша.
Используя такие инструменты, как PostSharp , вы можете настроить его так, чтобы любой метод, отмеченный выбранным атрибутом, был кэширован. Однако, если это единственное, что вы кэшируете, вам не нужно заходить так далеко, чтобы иметь AOP-фреймворк. Просто имейте Repository и Caching Wrapper, которые используют один и тот же интерфейс, и вставьте его в вызывающий класс.
например.
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
}
}
Посмотрите, как вы удалили знания о реализации репозитория из ProductManager? Посмотрите также, как вы придерживались принципа единой ответственности, имея класс, который обрабатывает извлечение данных, класс, который обрабатывает извлечение данных, и класс, который обрабатывает кэширование?
Теперь вы можете создать экземпляр ProductManager с любым из этих репозиториев и получить кеширование ... или нет. Это невероятно полезно позже, когда вы получаете запутанную ошибку, которая, как вы подозреваете, является результатом кэша.
productManager = new ProductManager(
new SqlProductRepository()
);
productManager = new ProductManager(
new CachedProductRepository(new SqlProductRepository())
);
(Если вы используете контейнер IOC, даже лучше. Должно быть очевидно, как адаптироваться.)
И в ваших тестах ProductManager
IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();
Не нужно тестировать кеш вообще.
Теперь возникает вопрос: я должен проверить этот CachedProductRepository? Я предлагаю нет. Кеш довольно неопределенный. Фреймворк делает с ним вещи, которые находятся вне вашего контроля. Например, просто убрать из него вещи, когда они переполнятся, например. Вы закончите тестами, которые не пройдут однажды в голубой луне, и вы никогда не поймете почему.
И, внеся изменения, которые я предложил выше, на самом деле не так уж много логики для тестирования. Там будет действительно важный тест, метод фильтрации, который будет полностью абстрагирован от деталей GetAll (). GetAll () просто ... получает все. Откуда-то