crli
Oct 16, 2008
  1400
(0 votes)

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

Comments

Oct 12, 2010 10:34 AM

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

Oct 12, 2010 10:34 AM

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.
Latest blogs
Customizing Property Lists in Optimizely CMS

Generic property lists is a cool editorial feature that has gained a lot of popularity - in spite of still being unsupported (officially). But if y...

Allan Thraen | Oct 2, 2022 | Syndicated blog

Content Delivery API – The Case of the Duplicate API Refresh Token

Creating a custom refresh provider to resolve the issues with duplicate tokens in the DXC The post Content Delivery API – The Case of the Duplicate...

David Lewis | Sep 29, 2022 | Syndicated blog

New Optimizely certifications - register for beta testing before November 1st

In January 2023, Optimizely is making updates to the current versions of our certification exams to make sure that each exam covers the necessary...

Jamilia Buzurukova | Sep 28, 2022

Optimizely community meetup - Sept 29 (virtual + Melbourne)

Super excited to be presenting this Thursday the 29th of September at the Optimizely community meetup. For the full details and RSVP's see the...

Ynze | Sep 27, 2022 | Syndicated blog

Preview multiple Visitor Groups directly while browsing your Optimizely site

Visitor groups are great - it's an easy way to add personalization towards market segments to your site. But it does come with it's own set of...

Allan Thraen | Sep 26, 2022 | Syndicated blog

The Report Center is finally back in Optimizely CMS 12

With Episerver.CMS.UI 12.12.0 the Report Center is finally re-introduced in the core product.

Tomas Hensrud Gulla | Sep 26, 2022 | Syndicated blog