In part-one of this two-part mini series I covered how to get acceptance tests written using Selenium working as part of the deployment pipeline. In that post the focus was on configuring the moving parts needed to get some existing acceptance tests up-and-running with the new Release Management tooling in TFS or VSTS. In this post I make good on my promise to explain how to use SpecFlow and Selenium together to write business readable web tests as opposed to tests that probably only make sense to a developer.
If you haven’t used SpecFlow before then I highly recommend taking the time to understand what it can do. The SpecFlow website has a good getting started guide here however the best tutorial I have found is Jason Roberts’ Automated Business Readable Web Tests with Selenium and SpecFlow Pluralsight course. Pluralsight is a paid-for service of course but if you don’t have a subscription then you might consider taking up the offer of the free trial just to watch Jason’s course.
As I started to integrate SpecFlow in to my existing Contoso University sample application for this post I realised that the way I had originally written the Selenium-based page object model using a fluent API didn’t work well with SpecFlow. Consequently I re-wrote the code to be more in line with the style used in Jason’s Pluralsight course. The versions are on GitHub – you can find the ‘before’ code here and the ‘after’ code here. The instructions that follow are written from the perspective of someone updating the ‘before’ code version.
Install SpecFlow Components
To support SpecFlow development, components need to be installed at two levels. With the Contoso University sample application open in Visual Studio (actually not necessary for the first item):
- At the Visual Studio application level the SpecFlow for Visual Studio 2015 extension should be installed.
- At the Visual Studio solution level the ContosoUniversity.Web.AutoTests project needs to have the SpecFlow NuGet package installed.
You may also find if using MSTest that the specFlow section of App.config in ContosoUniversity.Web.AutoTests needs to have an <unitTestProvider name=”MsTest” /> element added.
Update the Page Object Model
In order to see all the changes I made to update my page object model to a style that worked well with SpecFlow please examine the ‘after’ code here. To illustrate the style of my updated model, I created CreateDepartmentPage class in ContosoUniversity.Web.SeFramework with the following code:
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
namespace ContosoUniversity.Web.SeFramework
{
public class CreateDepartmentPage
{
[FindsBy(How = How.Id, Using = "Name")]
private IWebElement _name;
[FindsBy(How = How.Id, Using = "Budget")]
private IWebElement _budget;
[FindsBy(How = How.Id, Using = "StartDate")]
private IWebElement _startDate;
[FindsBy(How = How.Id, Using = "InstructorID")]
private IWebElement _administrator;
[FindsBy(How = How.XPath, Using = "//input[@value='Create']")]
private IWebElement _submit;
public CreateDepartmentPage()
{
PageFactory.InitElements(Driver.Instance, this);
}
public static CreateDepartmentPage NavigateTo()
{
Driver.Instance.Navigate().GoToUrl("http://" + Driver.BaseAddress + "/Department/Create");
return new CreateDepartmentPage();
}
public string Name
{
set
{
_name.SendKeys(value);
}
}
public decimal Budget
{
set
{
_budget.SendKeys(value.ToString());
}
}
public DateTime StartDate
{
set
{
_startDate.SendKeys(value.ToString());
}
}
public string Administrator
{
set
{
_administrator.SendKeys(value);
}
}
public DepartmentsPage Create()
{
_submit.Click();
return new DepartmentsPage();
}
}
}
The key difference is that rather than being a fluent API the model now consists of separate properties that more easily map to SpecFlow statements.
Add a Basic SpecFlow Test
To illustrate some of the power of SpecFlow we’ll first add a basic test and then make some improvements to it. The test should be added to ContosoUniversity.Web.AutoTests – if you are using my ‘before’ code you’ll want to delete the existing C# class files that contain the tests written for the earlier page object model.
- Right-click ContosoUniversity.Web.AutoTests and choose Add > New Item. Select SpecFlow Feature File and call it Department.feature.
- Replace the template text in Department.feature with the following:
Feature: Department
In order to expand Contoso University
As an administrator
I want to be able to create a new Department
Scenario: New Department Created Successfully
Given I am on the Create Department page
And I enter a randomly generated Department name
And I enter a budget of £1400
And I enter a start date of today
And I enter an administrator with name of Kapoor
When I submit the form
Then I should see a new department with the specified name
- Right-click Department.feature in the code editor and choose Generate Step Definitions which will generate the following dialog:
- By default this will create a DepartmentSteps.cs file that you should save in ContosoUniversity.Web.AutoTests.
- DepartmentSteps.cs now needs to be fleshed-out with code that refers back to the page object model. The complete class is as follows:
using System;
using TechTalk.SpecFlow;
using ContosoUniversity.Web.SeFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ContosoUniversity.Web.AutoTests
{
[Binding]
public class DepartmentSteps
{
private CreateDepartmentPage _createDepartmentPage;
private DepartmentsPage _departmentsPage;
private string _departmentName;
[Given(@"I am on the Create Department page")]
public void GivenIAmOnTheCreateDepartmentPage()
{
_createDepartmentPage = CreateDepartmentPage.NavigateTo();
}
[Given(@"I enter a randomly generated Department name")]
public void GivenIEnterARandomlyGeneratedDepartmentName()
{
_departmentName = Guid.NewGuid().ToString();
_createDepartmentPage.Name = _departmentName;
}
[Given(@"I enter a budget of £(.*)")]
public void GivenIEnterABudgetOf(int p0)
{
_createDepartmentPage.Budget = p0;
}
[Given(@"I enter a start date of today")]
public void GivenIEnterAStartDateOfToday()
{
_createDepartmentPage.StartDate = DateTime.Today;
}
[Given(@"I enter an administrator with name of Kapoor")]
public void GivenIEnterAnAdministratorWithNameOfKapoor()
{
_createDepartmentPage.Administrator = "Kapoor";
}
[When(@"I submit the form")]
public void WhenISubmitTheForm()
{
_departmentsPage = _createDepartmentPage.Create();
}
[Then(@"I should see a new department with the specified name")]
public void ThenIShouldSeeANewDepartmentWithTheSpecifiedName()
{
Assert.IsTrue(_departmentsPage.DoesDepartmentExistWithName(_departmentName));
}
[BeforeScenario]
public void Init()
{
Driver.Initialize();
}
[AfterScenario]
public void Cleanup()
{
Driver.Close();
}
}
}
If you take a moment to examine the code you’ll see the following features:
- The presence of methods with the BeforeScenario and AfterScenario attributes to initialise the test and clean up afterwards.
- Since we specified a value for Budget in Department.feature a method step with a (poorly named) parameter was created for reusability.
- Although we specified a name for the Administrator the step method wasn’t parameterised.
As things stand though this test is complete and you should see a NewDepartmentCreatedSuccessfully test in Test Explorer which when run (don’t forget IIS Express needs to be running) should turn green.
Refining the SpecFlow Test
We can make some improvements to DepartmentSteps.cs as follows:
In the preceding change note the change to both the attribute and the method name.
Updating the Build Definition
In order to start integrating SpecFlow tests in to the continuous delivery pipeline the first step is to update the build definition, specifically the AcceptanceTests artifact that was created in the previous post which needs amending to include TechTalk.SpecFlow.dll as a new item of the Contents property. A successful build should result in this dll appearing in the Artifacts Explorer window for the AcceptanceTests artifact:
Update the Test Plan with a new Test Case
If you are running your tests using the test assembly method then you should find that they just run without and further amendment. If on the other hand you are using the test plan method then you will need to remove the test cases based on the old Selenium tests and add a new test case (called New Department Created Successfully to match the scenario name) and edit it in Visual Studio to make it automated.
And Finally
Do be aware that I’ve only really scratched the surface in terms of what SpecFlow can do and there’s plenty more functionality for you to explore. Whilst it’s not really the subject of this post it’s worth pointing out that when deciding to adopt acceptance tests as part of your continuous delivery pipeline it’s worth doing so in a considered way. If you don’t it’s all too easy to wake up one day to realise you have hundreds of tests which take may hours to run and which require a significant amount of time to maintain. To this end do have a listen to Dave Farley’s QCon talk on Acceptance Testing for Continuous Delivery.
Cheers – Graham
The post Continuous Delivery with TFS / VSTS – Automated Acceptance Tests with SpecFlow and Selenium Part 2 appeared first on Please Release Me.