Friday, 14 January 2011

Acceptance Tests, Fluent Interfaces and a DSL

Lately ive been doing quite a lot of acceptance testing. We did give some BDD frameworks a try especially SpecFlow, but were left with a feeling that it was too complex for the benifits that it gave, was too inflexable and in the long run could turn into a bit of a mess. well that was the evaluation of the team, im sure there are good ways of doing it that is valuble but we decided not to go with a framework.

So i came up with a DSL that gives the developer a simple 'Given When Then' syntax that im quite pleased with, it allows for the most readable / maintainable acceptance test ive seen to date.

A small acceptance test follows as an example:

using NUnit.Framework;
using OpenQA.Selenium;
using AcceptanceTests.Domain;

namespace AcceptanceTests.Fixtures
{
    [TestFixture]
    public class TagTests : BaseTestFixture
    {
        [Test]
        public void ThisAndThatShouldDoSomeThings()
        {
            var resultsPage = Page.SiteABC.ResultsPage;
            given.WeAreOnThePage(resultsPage);

            when.I.Click.TheElement(By.LinkText("civil-engineering"));

            then.TheUrlShouldBe(Page.SiteABC.Article("civil-engineering"))
                .And.TheTitleShouldBe("civil-engineering articles in ABC example page");
            then.TheElement(By.ClassName("tag-wrapper")).ContainsTheText("Articles with the Tag:\r\ncivil engineering");

            when.I.Type("london").Into(By.Id("txtLocation"))
                .And.I.Click.TheElement(By.LinkText("search"));

            then ..... (You get the idea)
        }
    }
}

The base class gives the test fixtures access to the Given When Then classes similar to this:

public class BaseTestFixture
{
    protected Given given;
    protected When when;
    protected Then then;

    [TestFixtureSetUp]
    public void SetUp()
    {
        given = new Given();
        when = new When();
        then = new Then();
    }
}

The actual Given When Then classes delegate the actions to webdriver in the following manner:


public class Given
{
    private readonly WebDriver webDriver;

    public Given()
    {
        webDriver = WebDriver.GetCurrent();
    }

    public Given And
    {
        get { return this; }
    }

    public Given WeAreOnThePage(Page page)
    {
        webDriver.Driver.Navigate().GoToUrl(page.Url);
        return this;
    }
}


public class When
{
    private readonly WebDriver webDriver;

    public When()
    {
        this.webDriver = WebDriver.GetCurrent();
    }

    public When And
    {
        get { return this; }
    }

    public When I
    {
        get {return this; }
    }

    public IAction Click
    {
        get { return new Click(this); }
    }

    public TypeText Type(string text)
    {
        return new TypeText(text);
    }
}


By writing the step classes like this we can write the tests in a fluent style, making for very readable. This is important as it enables none technical (business) people to read / understand the tests and have input on the creation of the tests, although i admit it still does not really enable them to write them, as you do need to know c# and visual studio, but in my experience they never write them anyway (especially on the contact im on), its technical people who write the automated acceptance tests.

All in all im quite pleased who the suite of tests is progressing and how the DSL has evolved.

1 comment: