Views: 1115
Number of votes: 0
Average rating:

R2 and unit testing

I've been exploring the unit testing story in the latest EPiServer release and so far I've been pleasantly surprised. R2 brings some improvements in this respect. Most of the time I can execute my tests without chatting with the database or faking the http context.

The key to this is the page provider. I'll briefly cover the basics of setting up a test with a fake page provider and offer tools to help you along.

Why unit test? A healthy set of unit tests has saved my butt more than a few times and if you're at least half-serious about software development you should be looking after yours.

How to do it

Step 0: envision the business requirements

"As a user I can submit a suggestion so that a support representative can review it and publish it for everyone to vote"

Step 1: write a test

For the purpose of this example I'm choosing NUnit because of the nice fluent syntax helpers. The test creates a suggestion and submits it to the suggestion box. Then we assert that the suggestion can be retrieved.

    [TestFixture]
    public class SubmittingSuggestions
    {
        [Test]
        public void WhenSuggestionIsMade_ItsAddedToTheBacklog()
        {
            SuggestionBox box = new SuggestionBox();
            Suggestion original = new Suggestion("Make it stop", "I see no end to the possibilities");
            box.Submit(original);

            Suggestion retrieved = box.Dequeue();

            Assert.That(retrieved.Title, Is.EqualTo(original.Title));
            Assert.That(retrieved.Body, Is.EqualTo(original.Body));
        }
    }

Step 2: make it pass

A simple suggestion box implementation:

    public class Suggestion
    {
        public Suggestion(string questionTitle, string questionBody)
        {
            Title = questionTitle;
            Body = questionBody;
        }

        public string Title { get; set; }
        public string Body { get; set; }
    }

    public class SuggestionBox
    {
        Queue<Suggestion> questions = new Queue<Suggestion>();

        public void Submit(Suggestion suggestion)
        {
            questions.Enqueue(suggestion);
        }

        public Suggestion Dequeue()
        {
            return questions.Dequeue();
        }
    }

Step 3: refactor

The test passes but it's not a terribly permanent way to store our suggestions. For the purpose of a real application this is just a fancy way of creating heat. We need to go back to the test and think again.

Step 1: think again

We change the test to assume we store the questions as a page in an episerverp age container. Doing so is nice since it allows the support representative to review and publish the suggestion from within the edit interface.

        private PageReference boxLink;

        [SetUp]
        public void SetUp()
        {
            PageData box = DataFactory.Instance.GetDefaultPageData(PageReference.StartPage, SuggestionBoxPageTypeID);
            box.PageName = "Suggestion box";
            boxLink = DataFactory.Instance.Save(box, SaveAction.Publish, AccessLevel.NoAccess);
        }

        [Test]
        public void WhenSuggestionIsMade_ItsAdded_ToTheSuggestionBoxContainer()
        {
            SuggestionBox box = new SuggestionBox(boxLink, DataFactory.Instance);
            Suggestion original = new Suggestion("Make it stop", "I see no end to the possibilities");
            box.Submit(original);

            PageDataCollection stored = DataFactory.Instance.GetChildren(boxLink);

            Assert.That(stored[0].PageName, Is.EqualTo(original.Title));
            Assert.That(stored[0]["MainBody"], Is.EqualTo(original.Body));
        }

Step 2: make it pass

Now the fun begins. We've already introduced a reference to EPiServer but we need to initialize it. For this purpose I've created a little helper that creates a fake page provider that doesn't use the database. I won't go over the details but the can be downloaded at the end of the article. For now we just reference EPiSugar.Utilities and tweak the test (sorry kent):

        [SetUp]
        public void SetUp()
        {
            EPiSugar.UnitTesting.Fakes.Initialize(new FakeStructureBuilder(1, 0));
            ...

As you would soon discover, we need a few more things:

  • A license.config whose properties is set to "Copy to Output Directory": "Copy if newer"
  • And an app configuration: for this I'm just using the contents of my web.config and adding connectionStrings.config

This is how the new and improved implementation looks like:

public class SuggestionBox
{
    private const int SuggestionPageTypeID = 11;
    private readonly PageReference containerLink;
    private readonly DataFactory dataFactory;
    public SuggestionBox(PageReference containerLink, DataFactory dataFactory)
    {
        this.containerLink = containerLink;
        this.dataFactory = dataFactory;
    }

    public void Submit(Suggestion suggestion)
    {
        PageData page = dataFactory.GetDefaultPageData(containerLink, SuggestionPageTypeID);
        page.PageName = suggestion.Title;
        page["MainBody"] = suggestion.Body;
        dataFactory.Save(page, SaveAction.Publish, AccessLevel.NoAccess);
    }

    public Suggestion Dequeue()
    {
        throw new NotImplementedException();
    }
}

Time to run some tests... Wohoo!: 1 passed, 0 failed, 0 skipped

Step 3: left as an excercise to the reader

Conclusion

Don't let the simplistic example put you off. One simple test at the time we can achieve great feats. I can't say such tests arn't ideal for UI testing in my experience. Would you have any experiences to share in this area?

Using a faked page provider can really help speed up the tests and smoothe the pain of setting up test data. The great thing is that we don't need to modify the database state. EPiserver has reached a long way in terms of testability. It's far from perfect, but the road never ends. Doesn't it?

Download the example test + the EPiSugar utility. You can also get it directly from the source code repository. Hope this can be useful to you.

Oct 16, 2008

Guest
(By Guest, 10/12/2010 12:34:58 PM)

Nice to find you blogging again :-) Btw, what's episerver anyway ? I've heard it before, but hadn't time to investigate any further. Codeplex seems to contain just stubs for that.
/ esteewhy

Guest
(By Guest, 10/12/2010 12:34:59 PM)

Thank you SO much for this, EXTREMELY helpful. Unit testing episerver is certainly not the easiest thing to get started with. /M
/ Mattias Petter Johansson

Please login to comment.