mixing web, code and humans by Emil Cardell
UNWILLINGCODER
The Unwilling Coder is...

...unwilling to create and develop crappy solutions, always working for a better one.
...unwilling to pass blame, by taking responsibility to solve the problem.
...unwilling to accept subjective or uninformed decisions, gathering info to make objective decisions.
...unwilling to accept a project without full transparency, working for an open and visual workspace.
...unwilling to do the same thing over and over again, evolving my work every day to find a better ways.

Published 2009-09-14 07:03 in The world of EPiServer

Unit testing the cache in Episerver Community

One irritating problem that can occur more than you like when you are working with EPiServer community is that the cache is not updated when it is supposed to. In my latest project we found a way to test the cache on community modules that you have developed yourself. In this example I’m going to use Rhino Mocks

Prepare your module


If you follow the standard pattern EPiServer uses on all there modules you will end up having a Factory that handles all the database queries, you need to make all methods used by the manager virtual and non static to be able to fake the methods on the factory. The standard pattern can look something like this, written on a whiteboard in my office:

community-diagram
hand drawn by Joel Abrahamsson

Here is an example of the factory

public class EntityDataFactory : FrameworkFactoryBase
{

    public virtual Entity GetEntity(int id)
    {
        //Some code
    }

  
}

When that's done you need to define a static factory member in your Manager to access the Factory its important to make it setable so you easly can define your own datafactory. An example: 

private static EntityDataFactory _dataFactoryInstance = new EntityDataFactory();

public static EntityDataFactory DataFactoryInstance
{
    get
    {
        return _dataFactoryInstance;
    }
    set
    {
        _dataFactoryInstance = value;
        _getEntityDelegate = _dataFactoryInstance.GetEntity;
        _getEntityForDelegate = _dataFactoryInstance.GetEntityFor;
    }

}

Enable the and setting up the cache


The asp.net cache needs httpcontext to be inizialised to work so we need to initialise it.

public static void InitCurrentHttpContext()
{
    TextWriter tw = new StringWriter();
    HttpWorkerRequest wr = new SimpleWorkerRequest(
      "/",
      "C:\\",
      "default.aspx", "", tw);
    HttpContext.Current = new HttpContext(wr);
}

public static void ClearCurrentContextCache()
{
    if (HttpContext.Current != null)
    {
        foreach (DictionaryEntry cache in HttpContext.Current.Cache)
            HttpContext.Current.Cache.Remove(cache.Key.ToString());

        return;
    }
}

Now when we access the cache it will not be configured and it will give us errors. We can try to start the whole community framework but that is time consuming task and we want our test to be as fast as possible. I used the Reflector to find which variables to be set and used reflection to sett the correct values.

public static void InitCache(HttpContext context)
{
    Type type = typeof(CacheHandler);
    FieldInfo infoCache = type.GetField("m_cache", BindingFlags.NonPublic | BindingFlags.Static);
    infoCache.SetValue(null, HttpContext.Current.Cache);

    FieldInfo info = type.GetField("m_cacheExpirationProvider", BindingFlags.NonPublic | BindingFlags.Static);

    CacheExpirationProvider provider = new CacheExpirationProvider();
    provider.Initialize(new NameValueCollection());
    info.SetValue(null, provider);
}

public static void InitCacheAndHttpContextWithAClearCache()
{
    if (HttpContext.Current != null)
        ClearCurrentContextCache();
    else
        InitCurrentHttpContext();
    
    InitCache(HttpContext.Current);
    
}

Testing and faking(mocking and stubbing)


Now all that's left is to create the test and mock the data factory. Our goal here is too see how many times we have accessed a method on the datafactory to determine if the cache is used or not. First we need to create a mock off the factory:

public static EntityDataFactory GetFakeEntityDataFactoryAndSetUpCache()
{
    EpiCommunityCacheSetup.InitCacheAndHttpContextWithAClearCache();

    MockRepository fakes = new MockRepository();
    EntityDataFactory factory = fakes.Stub<EntityDataFactory>();

    factory.Stub(x => x.GetEntity(FAKE_ENTITY_ID)).Return(FAKE_ENTITY);            

    EntityHandler.DataFactoryInstance = factory;
    return factory;
}

Then we build a test using the AAA(Arrange, Act, Assert) principle. Here I'm trying to test that the get method on the datafactory only get called once and the second time the cache should take the hit.

[Fact]
public void GivenNoPreviousGetBy_WhenGetIsCalled_ThenDataFactoryCalled()
{
    EntityDataFactory fakeFactory = FakeFactory.GetFakeEntityDataFactoryAndSetUpCache();

    EntityHandler.GetEntityFor(FakeFactory.FAKE_REMOTESITENAME, FakeFactory.FAKE__ID);
    EntityHandler.GetEntityFor(FakeFactory.FAKE_REMOTESITENAME, FakeFactory.FAKE__ID);

    fakeFactory.AssertWasCalled(factory => factory.GetEntityFor(FakeFactory.FAKE_REMOTESITENAME, FakeFactory.FAKE__ID));
}


Hopes this helps you make your EpiServerCommunity modules a bit more testable.

"@karolikl Nhibernate came along. :)"
about 7 hours ago
2 comments
Disagree, agree or got a question?
Make a comment

Email Twitter