UI-testing (EPiServer): SpecFlow

This post is not going to be about what BDD / Specification by example is and the benefits of it but more of a quick start with doing UI-testing in a SpecFlow manner.

Installing SpecFlow

Although it’s not technically necessary I’d advice you to add the SpecFlow extension to Visual Studio. This is added through TOOLS / Extensions and Updates.

sf01

Go to the online tab and search for SpecFlow and install it

sf02

The first feature

A feature is something we use to describe what we’re about to test and why. Since we’ve installed the SpecFlow extension we can simple add a new feature by adding a new item and select the SpecFlow Feature file. Since we’re going to continue with our header test let’s call it Header.feature.

sf03

Back to the feature

Our feature now contains the template feature so let’s take a look at it since it describes the anatomy of a feature quite nicely

Feature: Header
	In order to avoid silly mistakes
	As a math idiot
	I want to be told the sum of two numbers

@mytag
Scenario: Add two numbers
	Given I have entered 50 into the calculator
	And I have entered 70 into the calculator
	When I press add
	Then the result should be 120 on the screen

At the top of the feature is a description of the feature in a user story format. After the description comes one or more scenarios which is used to describe various aspects of the feature. The scenarios are what are actually executed as test(s). Each of the sentences that starts with a gherkin (that’s the name of the language) keyword (given, and, when, then) is called a step and is roughly equivalent to a method that executes that given task. (The example also has a tag, (@mytag), which we’ll talk about in another post.)

In our case of the header it’s likely that it does more than simply indicate the current active page so in real life we’d probably have more than the one scenario we’re going to focus on in this example.

Feature: Header
	In order to navigate the site
	As a visitor
	I want to be able to access the main sections of the site

Scenario: Active page
	Given I am on the page "/"
	Then the active link in the header should say "Start"
	When I am on the page "/Alloy-Plan"
	Then the active link in the header should say "Alloy Plan"

There’s much to be said about how the descriptions and scenarios should be formatted but since that’s a bit of a rabbit hole and (at least) a post in its own I’m gonna say “Look behind you, a three-headed monkey!” and get on with it.

Step definitions

With out first scenario written we need to make sure that there are steps corresponding to the ones specified in the feature file. Luckily the SpecFlow-tooling we installed earlier makes it dead simple to generate a skeleton to use. Simply right click on the scenario and choose “Generate step definitions”

sf04

In the window opening we can preview the missing steps SpecFlow found as well as some other options we can ignore for now. Click generate and save the file somewhere in your project.

sf06

The file that gets generated looks like this

[Binding]
public class HeaderSteps
{
    [Given(@"I am on the page ""(.*)""")]
    public void GivenIAmOnThePage(string p0)
    {
        ScenarioContext.Current.Pending();
    }
        
    [When(@"I am on the page ""(.*)""")]
    public void WhenIAmOnThePage(string p0)
    {
        ScenarioContext.Current.Pending();
    }
        
    [Then(@"the active link in the header should say ""(.*)""")]
    public void ThenTheActiveLinkInTheHeaderShouldSay(string p0)
    {
        ScenarioContext.Current.Pending();
    }
}

A step method

Let’s dissect the Given method to see what’s going on here.

[Given(@"I am on the page ""(.*)""")]
public void GivenIAmOnThePage(string p0)
{
    ScenarioContext.Current.Pending();
}

The method is marked with a attribute containing a keyword (Given in this case) and a regular expression. This regexp is used to match what is written in the feature file. SpecFlow was also clever enough to realize that what we typed inside the quotation marks (an url in this case) is probably going to be used as a parameter for the test. This is captured in a group in the regexp and then sent to the method as the (generated) parameter p0.

When SpecFlow executes the step ‘Given I am on the page “/”‘ it will find a method marked with an attribute that matches that regexp (GivenIAmOnThePage) and then use the group in the regexp to give the parameter p0 the value of “/”.

Implementing the steps

We’ll make use of the SeleniumServer class from the previous post and as usual we’ll need both the Nunit and the Selenium.WebDriver package. We’ll also add the NUnit for SpecFlow package so that we can use NUnit for asserts in the steps.

Install-Package Selenium.WebDriver
Install-Package NUnit
Install-Package SpecFlow.NUnit

Previously we used NUnit hooks to open and close the browser before any test is run and after all tests are run as well as clearing cookies between tests. SpecFlow also contains these type of hooks and they are implemented by adding the Binding attribute to a class and then implement methods that are decorated with one of the hooks attributes.

[Binding]
public class SpecFlowHooks
{
    [BeforeTestRun]
    public static void Start()
    {
        SeleniumServer.Start();
    }

    [AfterScenario]
    public static void AfterScenario()
    {
        SeleniumServer.ResetBrowser();
    }

    [AfterTestRun]
    public static void Shutdown()
    {
        SeleniumServer.Stop();
    }        
}

With that infrastructure in place we can finally implement the steps and also refactor the parameter names a bit for clarity.

[Given(@"I am on the page ""(.*)""")]
public void GivenIAmOnThePage(string slug)
{
    SeleniumServer.Driver.Navigate().GoToUrl("http://alloy.demo.episerver.com" + slug);
}
        
[When(@"I am on the page ""(.*)""")]
public void WhenIAmOnThePage(string slug)
{
    SeleniumServer.Driver.Navigate().GoToUrl("http://alloy.demo.episerver.com" + slug);
}
        
[Then(@"the active link in the header should say ""(.*)""")]
public void ThenTheActiveLinkInTheHeaderShouldSay(string expectedActiveLinkText)
{
    var activeLinkText = SeleniumServer.Driver.FindElement(By.CssSelector("ul.nav li.active")).Text;
    Assert.That(activeLinkText, Is.EqualTo(expectedActiveLinkText));
}

And our scenario is now able to run and pass

sf07

This concludes our first look at SpecFlow and UI-testing. While I haven’t touched upon the subject in detail in this series of post there are ways to mitigate code duplication and page layout details leaking into your test code. One such way is using the Page Object pattern. During the last years I (and the rest of the team) used Cucumber I liked how the gherkin language tied into UI-testing and at times it can be useful almost in the same manner as a page object but on a different level of abstraction. It might not be very clear in this introduction post but hopefully I can come back later and elaborate more on this subject.