UI testing your EPiServer site with Selenium and SpecFlow

As you may be aware of, testing and EPiServer (and specifically combining the two) is something that is quite dear to me. There are some hurdles to come over when (unit) testing basically anything before DDS. But this has already been covered in about a gazillion blog posts. Since a lot of logic in most EPiServer sites usually falls on the UI-side of things and that the webforms inheritance makes it hard to write tests that interacts with pages it can be beneficial to write tests against the UI instead of spending a lot of time write abstractions and mocking away.

This post will be an introduction to writing specifications for your site in SpecFlow which are then executed with the help of Selenium.

Introducing SpecFlow

SpecFlow is a BDD tool which mission in their own words is “to provide a pragmatic and frictionless approach to Acceptance Test Driven Development and Behavior Driven Development for .NET projects today.”. In practice it uses the language Gherkin and aims to write specifications that are readable for business analysts and customers in general and not only programmers. So, an example of a file written in Gherkin could look like this:

Feature: Startpage
	In order to feel welcome
	As a visitor
	I want to see "Hello!"

Scenario: Go to the start page
	When I go to the start page
	Then I should see "Hello!"

It starts with a Feature that has a title (Startpage) and then a benefit/business value (In order to feel welcome) for someone (a visitor) who wants to accomplish something / has a goal (want to see Hello). So it’s very similar to a user story (or exactly similar if you’re adopted the syntax that starts with the benefit rather than ending with it). The feature is followed by one or more scenarios which ties in to the given scenario. In the project I’m currently working on we’re almost 1:1 mapping between our user stories and features files and then create the acceptance criterion for the story as scenarios.

Another very nice thing with Gherkin is that it’s possible to write your feature files in a number of languages (we’re using Swedish). If you have a particularly awesome customer you might even end up writing in lol-code which would make your scenario look like this:

OH HAI: Startpage
	In order to feel welcome
	As a visitor
	I want to see "Hello!"

MISHUN: Go to the start page
	WEN I go to the start page
	DEN I should see "Hello!"

Downloading and installing

After downloading the msi file from the download page and installing that you should end up with a folder under program files called TechTalk (the company that created SpecFlow) as well as visual studio integration. The VS integration gives you a few new templates files that are visible when you add a new file

You will also need a test framework that the specs will be validated against. The default one is NUnit but there are support for more runners (xUnit for instance). In this example I’ll use NUnit.

Creating a feature file

So, let’s add a feature file that looks exactly like the example above. SpecFlow will generate a code behind file (.feature.cs) with glorious “auto-generated” code which as always means “These are not the droids you’re looking for. Please don’t touch.”

If we look at our scenario it consists of a number of steps (2 in our case) and we have to somehow implement them with code. If you don’t know the syntax by heart, which is likely when you’re starting out, SpecFlow (and a test runner) can help you with the building blocks for your steps. What you want to do is simply run the dll in which you added your feature file to in your test runner of choice and it will generate code for you that should look like this:

When I go to the start page
-> No matching step definition found for the step. Use the following code to create one:
[Binding]
public class StepDefinitions
{
[When(@"I go to the start page")]
public void WhenIGoToTheStartPage()
{
    ScenarioContext.Current.Pending();
}
}

Then I should see "Hello!"
-> No matching step definition found for the step. Use the following code to create one:
[Binding]
public class StepDefinitions
{

[Then(@"I should see ""Hello!""")]
public void ThenIShouldSeeHello()
{
    ScenarioContext.Current.Pending();
}
}

So, SpecFlow is hinting to us that it can’t find code to execute for our steps but is nice enough to tell us how the code it expected should look like. So let’s copy that and create a new Steps folder under our Features folder and add a StartPageSteps.cs file with the following code

[Binding]
public class StartPageSteps
{
    [When(@"I go to the start page")]
    public void WhenIGoToTheStartPage()
    {
        ScenarioContext.Current.Pending();
    }

    [Then(@"I should see ""Hello!""")]
    public void ThenIShouldSeeHello()
    {
        ScenarioContext.Current.Pending();
    }
}

So in these methods SpecFlow wants us to implement some sort of code that corresponds to the steps in question. Since we don’t have anything there at the moment it’s marked as pending.

Selenium

There has already been a couple of posts in the EPiSphere that covers Selenium so I won’t go into lengths about it here. In our scenario we want to test if the start page contains a Hello message so basically we want to navigate to “/” and then make sure it contains the text in question.

SpecFlow plus Selenium

Strongly typed access to Selenium in the scenario context

SpecFlow keeps a context for it’s scenarios and this is preferred to share data between steps in instead of keeping member variables. We’ll want to access Selenium across methods so I’ve added the following extension methods to the ScenarioContext.

public static class SeleniumContextExtensions
{
    public const string SeleniumKey = "Selenium";

    public static ISelenium Selenium(this ScenarioContext scenarioContext)
    {
        return scenarioContext[SeleniumKey] as ISelenium;
    }

    public static void SetSelenium(this ScenarioContext scenarioContext, ISelenium selenium)
    {
        scenarioContext[SeleniumKey] = selenium;
    }
}

Starting and stopping Selenium

I’m using a few of the SpecFlow hooks to be able to start and stop Selenium. As long as you mark your class with Binding you can specify these hooks without needed to inherit or anything of the sort.

[Binding]
public static class SeleniumSupport
{
    [BeforeScenario]
    public static void BeforeScenario()
    {
        SeleniumServer.Start();
        var selenium = SeleniumServer.GetDefaultSelenium();
        selenium.Start();
        ScenarioContext.Current.SetSelenium(selenium);
    }
        
    [AfterFeature]
    public static void AfterFeature()
    {
        SeleniumServer.Stop();
    }
} 

Implementing the steps

Now we have the tools needed to implement the steps.

[Binding]
public class StartPageSteps
{
    [When(@"I go to the start page")]
    public void WhenIGoToTheStartPage()
    {
        ScenarioContext.Current.Selenium().Open("/");
    }

    [Then(@"I should see ""Hello!""")]
    public void ThenIShouldSeeHello()
    {
        ScenarioContext.Current.Selenium().IsTextPresent("Hello");
    }
}

and we have some green stuff

Closing thoughts

There are loads of stuff both with Gherkin and SpecFlow we haven’t covered here. Hopefully I’ll have the chance to get back to this and give some more information about how you can use this in testing edit mode of EPiServer since this is particularly tricky area to write (unit) tests for. This will also be a part of the Progressive EPiServer Template package.