More testability to the people with EPiAbstractions!

I just released EPiAbstractions, an open source project that wraps many of the classes and methods in EPiServer CMS and Community that access the database or file system to allow the user of the project to decouple their code from them, thereby making the code easier to test.

Background – The problem

I really like both EPiServer CMS and EPiServer Community, but like most great products they have their flaws. The greatest one of those for me as a developer is that I’m often forced to program against concrete implementations instead of abstractions as many methods in the frameworks are either static or non virtual and not part of an interface or abstract base class. That in turn hampers me from creating well designed code that abide by the SOLID principles. It also severely limits my ability to test my code.

Sure, I can write integration tests for my methods that tests the whole sequence (calls to framework methods, interactions with the cache and the database etc) of events triggered by calling my method, but writing unit tests, that is tests that only test a single unit (the code in my method) is almost impossible as my code relies on concrete implementations that can’t be replaced.

Let’s look at a simple example. Let’s say I have a method that takes a PageData object as parameter. The method does something to the PageData object and then saves it.

public void DoStuffAndSavePage(PageData page)
{
    //Do stuff

    DataFactory.Instance.Save(page, SaveAction.Publish);
}

Now, how do we test what this method does to the PageData object? And how do we test that it actually saves it? We get EPiServer CMS up and running with a HTTP context and access to a database. Then we call the method and finally we ask EPiServer CMS for the recently saved page.

There are a number of problems with this approach. First of all the test will take tens of seconds to run which will probably mean that we won’t run it as often as we should. Secondly our test tests a lot more than our method. It also tests DataFactory’s Save method, the cache, the database etc. Thirdly, as the test have a lot of dependencies except the code that is actually being tested it is very fragile and it may also be hard to get it up and running on other computers than the one it was originally created on. All in all, we end up having a test that we don’t trust and that no one runs as it’s to much hassle to get it up and running.

Now, what if we could rewrite our method so that it doesn’t directly depend on DataFactory.Instance? A simple implementation could look like the code below, though in a real world scenario the DataFactory object would probably be a constructor parameter instead of a method parameter.

public void DoStuffAndSavePage(PageData page, DataFactory dataFactory)
{
    //Do stuff

    dataFactory.Save(page, SaveAction.Publish);
}

Given that the Save method in the DataFactory class was virtual (that is, a class that inherits from DataFactory would be allowed to override it) we could easily test our method without testing or relying on anything else. We could simple create a class the inherits from DataFactory and override it’s Save method in a way that the Save method let’s our test know that it has been called and with what parameters. Our test could then assert that the page had been saved and it could also check that whatever the method that we are testing is supposed to do with the PageData object has been done.

With this approach our test will run in milliseconds, it will only test one single unit and it will be very easy to get it up and running on a colleagues computer. There’s just one problem, the Save method in DataFactory isn’t virtual.

The same goes for quite a lot of other methods in EPiServer CMS and EPiServer Community, though most methods on “factory classes” aren’t just non-virtual, they are also static.

Background – The solution

So, how do we write code that is easily testable when working with EPiServer CMS or EPiServer Community? Thinking of all those static or non-virtual methods it’s easy to feel disheartened. Luckily there’s a simple solution. We simply create a wrapper around the methods that we need to use, like this:

public class DataFactoryFacade
{
    public virtual PageReference Save(PageData page, SaveAction saveAction)
    {
        return DataFactory.Instance.Save(page, saveAction);
    }
}

Then we rewrite the method that we are trying to test to use our wrapper, or facade if you will, instead.

public void DoStuffAndSavePage(PageData page, DataFactoryFacade dataFactory)
{
    //Do stuff

    dataFactory.Save(page, SaveAction.Publish);
}

With this approach we are now able to unit test our code, enabling us to work with test driven development should we want to.

There’s just one big problem with this approach, it’s boring as hell to create the facades!

Enter EPiAbstractions

After having created a lot of these facades in many of the projects that I’ve been working on, both customer projects and the Page Type Builder project, I got tired of it. So I thought to myself, why not create a separate project with facades for every single class that I can imagine ever using? That way I would never have to write one of those facades again. EPiAbstractions was born.

EPiAbstractions version 1.0 consists of a total of 22 assemblies which together contains wrappers for the following classes.

EPiServer CMS
Category, Frame, LanguageBranch, PageDefinition, PageType, PageVersion, ScheduledJob, TabDefinition, UnifiedPathInfo, Subscription, VirtualRoles, UnifiedDirectory, UnifiedFile, CacheManager, DataFactory

EPiServer Common
CacheHandler, DatabaseHandler, EntityProviderHandler, ActivityLogHandler, AttributeHandler, AuthorHandler, CategoryHandler, GlobalizationHandler, LogHandler, QueryHandler, RatingHandler, ReportHandler, SiteHandler, TagHandler, VisitHandler

EPiServer Community
DefaultSecurity (CommunitySystem.CurrentContext.DefaultSecurity), SecurityHandler (Blog), BlogHandler, CalendarHandler, ClubHandler, ConnectionLinkHandler, ContactHandler, ContestHandler, DirectMessageHandler, DocumentArchiveHandler, ExpertHandler, ForumHandler, ImageActionHandler, ImageGalleryHandler, MoblogHandler, MyPageHandler, NewsFeedHandler, PollHandler, StarViralHandler, WebmailHandler, VideoGalleryHandler

For each of these classes EPiAbstractions contains an interface named I<className>Facade and a concrete implementation named <className>Facade. All of the methods in the concrete implementations are virtual. The concrete implementations also all have a property named Instance which is both gettable and settable. I would recommend using EPiAbstractions together with an IoC container but if you don’t want to do that the Instance properties might come in handy.

Disclaimer

I haven’t tested this version that much but as I didn’t write the code by hand, but created a tool that wrote most of the code for me, it should be pretty stable. There might however be places where I’ve made conceptual mistakes. There is probably also quite a few more classes that should be wrapped which I’ll add later. So, look at this release as a sort of beta version :)

As always any and all feedback is very welcome!

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Comments

  1. Marek Blotny's avatar

    Marek Blotny 12 months ago

    Hi Joel,

    Yes, you are 100% right here "it’s boring as hell to create the facades!" :)

    We are creating similar kind of classes as well, it's the only way for now to implement TDD. I'm happy to see that other people are thinking likewise. Well ... now it's time to download EPiAbstractions and give it a try!

    Nice post and v. good job!

  2. Jet Basrawi's avatar

    Jet Basrawi 11 months ago

    Hi Joel,

    Nice idea. Marek has helped us to get going with MVP via his blogs and by sending us a bit of code that have been a real help.

    We are now at the point where we need to mock the DataFactory and we are trying your EPiAbstractions. However we keep getting the following error System.ArgumentException: The application relative virtual path '~/' is not allowed here.

    I have posted more detail on the EPiAbstractions Codeplex discussion where it will probably be more usefully discussed.

    Could you let us know what we are doing wrong?

    Many thanks

    Jet

  3. Joel Abrahamsson's avatar

    Joel Abrahamsson 11 months ago

    I'll do my best at the forum!

Add a comment

Allowed tags: <b>, <em>, <quote cite="">, <code>, <c-sharp-code>, <css-code>, <sql-code>, <xml-code>, <javascript-code>. If you want to display code examples, please remember to write &lt; for < and &gt; for >.

Follow me on Twitter

  1. Blogged: Learning Scala part nine – Uniform Access http://bit.ly/bmUat8 1 days ago
  2. @mikaellundin Your blog is a never ending source of wisdom. And weird mathematical problems. :) 3 days ago
  3. Bookmarked: Validate XHtml 1.0 Strict as part of your build process « Mint http://bit.ly/bOhaZj 3 days ago
follow me

Latest comments

  1. Svante wrote "Yes, I noticed that it was a singleton, and I guess the real..." on Something to beware of when using EPiAbstractions and an IoC container
  2. Joel Abrahamsson wrote "Well, first of all you wont get any arguments from me regard..." on Something to beware of when using EPiAbstractions and an IoC container
  3. Svante wrote "Hmm... Since the issue really is with the public instance co..." on Something to beware of when using EPiAbstractions and an IoC container

About this site

This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.

read more