The Single Responsibility Principle is your best friend here.
First of all, move AllFromCache() into a repository class and call it GetAll(). That it retrieves from the cache is an implementation detail of the repository and shouldn't be known by the calling code.
This makes testing your filtering class nice and easy. It no longer cares where you're getting it from.
Second, wrap the class that gets the data from the database (or wherever) in a caching wrapper.
AOP is a good technique for this. It's one of the few things that it's very good at.
Using tools like PostSharp, you can set it up so that any method marked with a chosen attribute will be cached. However, if this is the only thing you're caching, you don't need to go as far as having an AOP framework. Just have a Repository and a Caching Wrapper that use the same interface and inject that into the calling class.
eg.
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
}
}
See how you've removed the repository implementation knowledge from the ProductManager? See also how you've adhered to Single Responsibility Principle by having a class that handles data extraction, a class that handles data retrieval and a class that handles caching?
You can now instantiate the ProductManager with either of those Repositories and get caching ... or not. This is incredibly useful later when you get a confusing bug that you suspect is a result of the cache.
productManager = new ProductManager(
new SqlProductRepository());
productManager = new ProductManager(
new CachedProductRepository(new SqlProductRepository()));
(If you're using an IOC container, even better. It should be obvious how to adapt.)
And, in your ProductManager tests
IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();
No need to test the cache at all.
Now the question becomes: Should I test that CachedProductRepository? I suggest not. The cache is pretty indeterminate. The framework does things with it that are out of your control. Like, just removing stuff from it when it gets too full, for example. You're going to end up with tests that fail once in a blue moon and you'll never really understand why.
And, having made the changes I've suggested above, there's really not that much logic to test in there. The really important test, the filtering method, will be there and completely abstracted from the detail of GetAll()
. GetAll()
just ... gets all. From somewhere.