EPiServer  /  CMS August 12, 2010

EPiAbstractions.Fakes – Create unit tests without mocking

The Fakes module in the EPiAbstractions project provides fake implementations of the IPageRepository, IPageReferenceFacade and IEPiServerContext interfaces allowing you to easily write unit tests for EPiServer CMS sites without having to create mock objects yourself.

The module that ties it all together when testing

I have previously written about the new version of EPiAbstractions in general, the Opinionated module and the FixtureSupport module. The final piece of the EPiAbstractions puzzle is the Fakes module with which you can basically create an in-memory site and thereby creating unit tests that are more easy to read and understand than otherwise when you have to do a lot of mocking black magic.

Components

The Fakes module currently consists of three classes, FakePageReferenceFacade, FakePageRepository and FakeEPiServerContext. The two first, FakePageReferenceFacade and FakePageRepository are basically in-memory implementations of the IPageReferenceFacade and IPageRepository interfaces. If you instantiate them they won’t contain any data but you can certainly use them. For instance you can do things like this:

var page = CreatePage.OfType<PageData>();

var repository = new FakePageRepository();
                      
var pageRef = repository.Publish(page);

page = repository.GetPage(pageRef);

FakeEPiServerContext

To make things really easy when testing the FakeEPiServerContext class comes with a factory method, Create, that will not only return an implementation of the IEPiServerContext interface but will also set up it’s containing PageReference and PageRepository with a root page, a wastebasket page and a start page.

To get the start page you can do this:

cmsContext = FakeEPiServerContext.Create();

var startPage = cmsContext.PageRepository
    .GetPage(cmsContext.PageReference.StartPage);

// result will be true
var result = startPage.ParentLink == 
    cmsContext.PageReference.RootPage;
The Create method also has a typed overload with which you can specify the type of the start page.
cmsContext = FakeEPiServerContext.Create<StartPage>();

var startPage = cmsContext.PageRepository
    .GetPage<StartPage>(cmsContext.PageReference.StartPage);

Limitations to FakePageRepository

Currently there is some work that remains to be done as I have basically implemented the different methods in FakePageRepository as I’ve needed them myself or as someone else has expressed a need for them. Those that aren’t implemented, primarily overloads that take a language selector as a parameter, will throw a NotImplementedException. Also, the methods that normally filter by access rights and publication status doesn’t do that at all.

A real world usage example

In my post about the Opinionate module I used an example of a presenter for a breadcrumbs widget. A test for that presenter written using MSpec that utilizes the Fakes module could look like this:

[Subject("Breadcrumbs")]
public class when_viewing_a_page_that_is_child_of_the_startpage
{
    static TypedPageData currentPage;
    static FakeView<BreadcrumbsModel> view;

    Establish context
        = () =>
        {
            var cmsContext = FakeEPiServerContext.Create();

            currentPage = CreatePage.OfType<TypedPageData>()
                .ThatIs().ChildOf(cmsContext.PageReference.StartPage)
                .AndHas().PageName("da page name");

            cmsContext.PageRepository.Publish(currentPage);

            view = new FakeView<BreadcrumbsModel>();

            new BreadCrumbsPresenter(view, currentPage, cmsContext);

            view.RaiseLoadEvent();
        };

    It should_contain_a_single_link
        = () =>
            view.Model.Breadcrumbs.Count.ShouldEqual(1);

    It should_contain_a_link_with_text_equal_the_current_pages_name
        = () =>
          view.Model.Breadcrumbs.First().Text
            .ShouldEqual(currentPage.PageName);
}

The syntax of MSpec aside I think that the setup of the test (the context delegate) is fairly easy to understand. We create a EPiServerContext, create a new page that is a child of the start page and has a specified name. Finally we publish the page, create view (using a utility class not included here), create a new presenter and raise the load event of the view. Compare that to how the context would have looked if we hadn’t been using EPiAbstraction’s Fakes and FixtureSupport modules and instead relied on the usage of a mocking framework (Moq in this case):

Establish context
    = () =>
    {
        var fakeContext = new Mock<IEPiServerContext>();

        var fakePageReference = new Mock<IPageReferenceFacade>();

        fakeContext.SetupGet(c => c.PageReference)
            .Returns(fakePageReference.Object);

        var startPageRef = new PageReference(1);

        fakePageReference.SetupGet(p => p.StartPage)
            .Returns(startPageRef);

        // We need to mock currentPage as it's of an
        //abstract type
        currentPage = new Mock<TypedPageData>().Object;
        currentPage.PageName = "da page name";
        currentPage.ParentLink = startPageRef;

        view = new FakeView<BreadcrumbsModel>();

        new BreadCrumbsPresenter(view, currentPage, fakeContext.Object);

        view.RaiseLoadEvent();
    };

Not only is the first version several lines shorter, I think it’s feels far more intuitive to read and captures the essence of the test/specification much better.

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.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

My book

Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!

More about EPiServer CMS