I have a service layer, which has a range of methods. These methods have implemented caching, like the following:
string key = "GetCategories";
if (CacheHandler.IsCachingEnabled() && !CacheHandler.ContainsKey(key))
{
var categories = RequestHelper.MakeRequest("get_category_index")["categories"];
var converted = categories.ToObject<List<Category>>();
CacheHandler.InsertToCache(key,converted);
return converted;
}
return CacheHandler.GetCache(key) as List<Category>;
Now, problem is I also want to make a unit test, like the following:
[TestMethod]
public void GetCategories()
{
IContentService contentService = new ContentService();
var resp = contentService.GetCategories();
Assert.IsNotNull(resp,"Should not be null");
}
Problem is, that the HttpContext.Current
inside my CacheHandler
is null during the unit test (obviously).
What is the easiest way to fix this?
(Please be as specific as possible because I haven't done a lot of unit testing before)
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.
Caching has been established as a vital mechanism for providing high scalability in modern distributed software systems. Each conventional web browser contains a caching systems which stores and reuses HTTP responses in order to reduce data traffic and communication latency.
To run MSTest unit tests, specify the full path to the MSTest executable (mstest.exe) in the Unit Testing Options dialog. To call this dialog directly from the editor, right-click somewhere in the editor and then click Options.
This screams dependency injection. The main problem I see is that you access the CacheHandler
statically, so in a unit test, you:
a) cannot test the service without "testing" the CacheHandler
as well
b) cannot supply any other CacheHandler
to the service, for example a mocked one
If that's possible in your case, I'd either refactor or at least wrap the CacheHandler
so that the service accesses an instance of it. In a unit test, you can then supply the service with a "fake" CacheHandler
, that would not access HttpContext and also could give you a very fine control over the test itself (e.g. you can test what happens when an item is cached vs. when it isn't in two absolutely independent unit tests)
For the mocking part, I suppose it's easiest to create an interface and then use some automocking/proxy-generation framework designed for testing, for example Rhino Mocks (but there are many more, it just happens that I'm using this one and am very happy with it :)). Another approach (easier for a beginner, but more cumbersome in an actual development) would be simply to design the CacheHandler
(or its wrapper) so that you can inherit from it and override the behaviour yourself.
Finally for the injection itself, I have found out a handy "pattern", which takes advantage of C# default method arguments and the standard constructor injection. The service constructor would look like:
public ContentService(ICacheHandler cacheHandler = null)
{
// Suppose I have a field of type ICacheHandler to store the handler
_cacheHandler = cacheHandler ?? new CacheHandler(...);
}
So in the application itself, I can call the constructor without parameters (or let frameworks construct the service, if it's ASP.NET handler, WCF service or some other kind of class) and in unit tests, I can supply whatever is implementing the said interface.
In case of Rhino Mocks, it can look like this:
var mockCacheHandler = MockRepository.GenerateMock<ICacheHandler>();
// Here I can mock/stub methods and properties, set expectations etc...
var sut = new ContentService(mockCacheHandler);
Dependency Injection as recommended in Honza Brestan's answer is certainly a valid solution, and maybe the best solution - especially if you might want to use something other than the ASP.NET Cache in the future.
However I should point out that you can use the ASP.NET Cache without needing an HttpContext
. Instead of referencing it as HttpContext.Current.Cache
, you can use the static property HttpRuntime.Cache.
This will enable you to use the Cache outside the context of an HTTP request, such as in a unit test or in a background worker thread. In fact I'd generally recommend using HttpRuntime.Cache
for data caching in the business tier to avoid taking the dependency on the existence of an HttpContext
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With