Using AutoFixture To Generate Anonymous Test Data in Web UI Automation With BDDfy and Seleno

I’m currently working on an AutoFixture Pluralsight course and it got me thinking about using it to generate anonymous test data when writing automated UI tests.

The basic premise is that in addition to using AutoFixture to generate unit test data, it can also be used to populate UI elements where the specific data values are unimportant.

If you are unfamiliar with BDDfy and Seleno, they are part of the TestStack project.

BDDfy allows tests to be written and to produce business readable documentation. Seleno allows the automation of web browsers using Selenium and strongly-typed page object models.

Example Scenario

Imagine that we have an (ASP.NET MVC) web site that allows the addition of members of the royal family.

image

If we wanted to test different king/queen names (but didn’t care about the Regnal number) we could start off by defining some strongly typed (Seleno) page object models:

public class HomePage : Page<AddRoyaltyModel>
{
    public CreatedPage CreateRoyalty(AddRoyaltyModel royal)
    {
        Input.Model(royal);

        return Navigate.To<CreatedPage>(By.Id("Create"));
    }
}

public class CreatedPage : Page
{
}

 

(all the examples in this post are fairly quick-and-dirty to demonstrate the AutoFixture involvement)

 

Next we can write some example in BDDfy (using xUnit.net as the test framework):

public class AddRoyaltyTests
{
    private HomePage _home;
    private CreatedPage _confirmationPage;
    public string Name { get; set; }
    public string Number { get; set; }

    [Fact]
    public void ShouldAddRoyaltiesWithDifferentNames()
    {
        this.Given(x => GivenIAmStartingANewRoyalAddition())
            .And("And I have entered <name> as the royalty name")
            .And("And I have entered <number> as the regnal number")
            .When(x => WhenIChooseToAddTheNewRoyal())
            .Then(x => ThenIShouldSeeAConfirmationOfTheNewRoyalHavingBeenAdded())
            .WithExamples(new ExampleTable("name", "number")
                          {
                              {"Richard", "I"},
                              {"Henry", "I"},
                              {"Elizabeth", "I"}
                          })
            .BDDfy();            
    }



    private void GivenIAmStartingANewRoyalAddition()
    {
        _home = Host.Instance.NavigateToInitialPage<HomeController, HomePage>(x => x.Index());
    }        

    private void WhenIChooseToAddTheNewRoyal()
    {
        var royal = new AddRoyaltyModel
        {
            Name = this.Name,
            RegnalRomanNumeral = this.Number
        };

        _confirmationPage = _home.CreateRoyalty(royal);
    }

    private void ThenIShouldSeeAConfirmationOfTheNewRoyalHavingBeenAdded()
    {
        Assert.Equal("Created ok", _confirmationPage.Title);
    }
}

Notice in the preceding code that even though we don’t care about the Regnal number we are still supplying it in the examples. We could just set it manually to a hardcoded value, and in this simple example that might be ok, but if we had a form with many fields then this  will introduce extra work and may make the test less “refactor-safe”.

The following code shows the removal of the Regnal name/number:

public class AddRoyaltyTestsUsingAutoFixture
{
    private HomePage _home;
    private CreatedPage _confirmationPage;
    public string Name { get; set; }

    [Fact]
    public void ShouldAddRoyaltiesWithDifferentNames()
    {
        this.Given(x => GivenIAmStartingANewRoyalAddition())
            .And("And I have entered <name> as the royalty name")
            .When(x => WhenIChooseToAddTheNewRoyal())
            .Then(x => ThenIShouldSeeAConfirmationOfTheNewRoyalHavingBeenAdded())
            .WithExamples(new ExampleTable("name")
                          {
                              "Richard",
                              "Henry",
                              "Elizabeth"
                          })
            .BDDfy();            
    }

    private void GivenIAmStartingANewRoyalAddition()
    {
        _home = Host.Instance.NavigateToInitialPage<HomeController, HomePage>(x => x.Index());
    }        

    private void WhenIChooseToAddTheNewRoyal()
    {
        var fixture = new Fixture();

        // Use AutoFixture to create anonymous data for all properties except
        // name which is set to the BDDfy example value
        var royal = fixture.Build<AddRoyaltyModel>()
            .With(x => x.Name, this.Name)
            .Create();

        _confirmationPage = _home.CreateRoyalty(royal);
    }

    private void ThenIShouldSeeAConfirmationOfTheNewRoyalHavingBeenAdded()
    {
        Assert.Equal("Created ok", _confirmationPage.Title);
    }
}

In this version, AutoFixture’s Build method is being used to automatically generate test data for all the fields, except the royal name which is set to the name(s) specified in the BDDfy examples.

Running this test results in the following automation:

SelenoAutofixture

 

And produces the following HTML BDDfy report:

image

Using Page Object Models in UI Test Automation

When using UI automation technologies, changes to the UI can break our tests. For example, if a textbox is being located by its ID in numerous tests, if this ID changes then all these tests will break. They won’t be able to find the ID.

Another problem that can arise with automated UI testing is code duplication. Imagine a system that had an color drop down list and an apply button in multiple places in the application. Each UI test that exercises these color choices must first locate the color choice box, select a color, locate the apply button, and then click it.

The 3 Levels Of User Interface Testing

3 Levels Of User Interface Testing

One way to think of the “evolution” of UI testing is that of: test script, page object model (POM), and logical functional model (LFM).

Test Script

Test scripts are self contained tests. They contain the whole test from starting the application through to the asserts at the end.

Test scripts can be lengthy as they have to include all the UI control finding and interaction code for every single step of the particular test. This can make them hard to read and understand exactly what behaviour is being tested.

Test scripts also don’t share any code with other tests, so code duplication can create additional maintenance effort over time.

Test scripts are an easy way to get started with automated UI testing, especially if you are learning the UI automation framework at the same time.

Pretty quickly, test scripts become unwieldy and so page object models can be introduced.

Page Object Models

Page object models (POM) represent a screen or page or group of controls in the UI that’s being tested.

For example, a screen with a color chooser could be represented by a POM class that exposes the drop down control and buttons as properties.

These properties encapsulate the locating of the UI dropdown and button (perhaps by ID). The test code itself then uses an instance of the POM to interact with the dropdown and button – it doesn’t have to worry about the UI IDs in the test itself.

There’s a couple of levels of abstraction within POMs. The property that represents a UI control can either expose the underlying automation framework object that represents the control, or a simple type such as string or int.

When first getting started with POMs you might want to go with the former approach until you get the idea of using POMs, though there’s not much additional work to implement the latter option.

By returning simple types rather than automation framework types, the actual test code can become more readable; they also help to abstract away the chosen automation framework.

Logical Functional Models

Logical function models (LFM) are an extension to POMs and may not deserve their own distinction in practice, however the distinction helps us to think how we can further enhance the readability of test code and reduce code duplication.

LFMs add methods to POMs. These methods represent logical actions that the end-user can perform on a given page.

The LFM methods can use the properties already defined on the POM.

For example, the logical user function of “I want to choose a color” can be represented as a single method in the POM, for example: ChooseColor(string color);

Within this single method, the existing POM properties representing the dropdown list of colors and button can be used.

Now, test code can simply write: myScreen.ChooseColor(“red”);

The LFM “style” further increases the ability to discern exactly what behaviour a given test is testing.

Building Page Object Models

When building a POM it’s a good idea to evolve it over time, adding properties and logical functional methods as needed by the tests.

The alternative is to model every control and user interaction on the page before starting writing test that use that POM. This increases the up-front cost of writing automated UI tests, you may not end up interacting with every UI control in your tests so it makes sense to evolve POMs in an iterative fashion.

When choosing a UI automation framework, investigate what built-in support it has for POMs/LFMs. It’s possible to hand-code them from scratch, but a little extra help from our chosen framework helps to reduce the cost of building UI automation tests.

 

To see POMs/LFMs in action in both WPF and Web UI automation, check out my Building the Right Thing in .NET with TestStack Pluralsight course.