Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have a number of business logic methods that store and retrieve (with filtering) objects and lists of objects from cache.

Consider

IList<TObject> AllFromCache() { ... }

TObject FetchById(guid id) { ... }

IList<TObject> FilterByPropertry(int property) { ... }

Fetch.. and Filter.. would call AllFromCache which would populate cache and return if it isn't there and just return from it if it is.

I generally shy away from unit testing these. What are best practices for Unit Testing against this type of structure?

I considered populating cache on TestInitialize and removing on TestCleanup but that doesn't feel right to me, (tho it could well be).

share|improve this question

6 Answers 6

up vote 12 down vote accepted

If you want true Unit Tests, then you have to mock the cache: write a mock object that implements the same interface as the cache, but instead of being a cache, it keeps track of the calls it receives, and always returns what the real cache should be returning according to the test case.

Of course the cache itself also needs unit testing then, for which you have to mock anything it depends on, and so on.

What you describe, using the real cache object but initializing it to a known state and cleaning up after the test, is more like an integration test, because you are testing several units in concert.

share|improve this answer
    
+1 this is defiantly the best approach. Unit test to check the logic then integration test to actually verify the cache works as you expect. –  Tom Squires Jan 29 '13 at 11:41

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.

share|improve this answer
    
What do you do if you're using CachedProductRepository in ProductManager but want to use methods that are in SQLProductRepository? –  Jonathan Aug 3 at 7:48
    
@Jonathan: "Just have a Repository and a Caching Wrapper that use the same interface" -- if they have the same interface, you can use the same methods. The calling code doesn't need to know anything about implementation. –  pdr Aug 4 at 9:11

Your suggested approach is what I'd do. Given your description, the result of the method should be the same whether the object is present in the cache or not: you should still get the same result. That's easy to test by setting up the cache in a particular way before each test. There are probably some additional cases like if the guid is null or no object has the requested property; those can be tested too.

Additionally, you may consider it expected that the object be present in the cache after your method returns, regardless of whether it was in the cache in the first place. This is contentious, as some people (myself included) would argue that you care about what you get back from your interface, not how you get it (i.e. your testing that the interface works as expected, not that it has a specific implementation). Should you consider it important, you have the opportunity to test that.

share|improve this answer

I considered populating cache on TestInitialize and removing on TestCleanup but that doesn't feel right to me

Actually, that is the only correct way to do. That is what those two functions are there for : to set the preconditions, and clean up. If the preconditions are not met, your program might not work.

share|improve this answer

I was working on some tests that use cacheing recently. I created a wrapper around the class that works with the cache, and then had assertions that this wrapper was being called.

I did this mainly because the existing class that works with cache was static.

share|improve this answer

It looks like you want to test the caching logic, but not the populating logic. So I would suggest that you mock what you don't need to test - populating.

Your AllFromCache() method takes care of populating the cache, and that should be delegated to something else, like a supplier of values. So your code would look like

private Supplier<TObject> supplier;

IList<TObject> AllFromCache() {
    if (!cacheInitialized) {
        //whatever logic needed to fill the cache
        cache.putAll(supplier.getValues());
        cacheInitialized = true;
    }

    return  cache.getAll();
}

Now you can mock the supplier for the test, to return some predefined values. That way, you can test your actual filtering and fetching, and not loading objects.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.