Best practices for unit testing methods that use cache heavily?

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).

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.

1

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.

2

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.

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.

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.

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.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật