Click here to Skip to main content
13,900,411 members
Click here to Skip to main content
Add your own
alternative version

Stats

9.1K views
6 bookmarked
Posted 24 Oct 2016
Licenced Ms-PL

Getting Started with SpecFlow in 10 Minutes

, 24 Oct 2016
Rate this:
Please Sign up or sign in to vote.
Use SpecFlow to define automated acceptance tests from business-readable specifications. Learn how to write understandable UI tests within 10 minutes.The post Getting Started with SpecFlow in 10 Minutes appeared first on Automate The Planet.

Introduction

I am happy to announce a new series of blog posts dedicated to Specflow. In the first article, I am going to introduce to you the Specflow framework. You will be able to write and execute business-readable specification after 10 minutes. Keep reading!

 

Definition

SpecFlow acceptance tests follow the BDD paradigm: define specifications using examples understandable to business users as well as developers and testers.

Installation and Setup

1. Install SpecFlow for Visual Studio (Open Extensions and Updates)

2. Install SpecFlow NuGet

3. Install SpecFlow for MSTest NuGet

4. Add Feature File

5. Create Your Scenarios

Before we proceed with the more technical stuff, I am going to explain what use case I am going to automate. Imagine that you are the nuclear engineer Guenter and you need to convert some metrics for example Kilowatt-hours to Newton-meters (you know nuclear stuff, can't I have some fun?).

Use Case

1. Navigate to Metric Conversions.

2. Navigate to Energy and Power Kilowatt-hours to Newton-meters conversion

3. Type Kilowatt-hours

4. Assert Newton-meters

Getting Started with Specflow in C#

In the feature file, I first describe what the feature needs to do. In my particular case, I want to use the scientific calculator to convert between different metrics. This is the power of SpecFlow it not only powers our tests but becomes application documentation too. Every scenario represents a different test or use case of the feature.

When the steps are displayed in purple, this means that we are not ready.  To be able to execute the scenario you need to define the so-called bindings for each step. We need to create step definitions that bind the statements in the test scenario to the application code. I will show you in a bit how to generate them automatically or create them manually. But first, let's create the necessary page objects that will drive our test.

Create Page Objects

You can read more about this page objects in my article- Page Objects That Make Code More Maintainable. We need two pages- home page and Kilowatt-hours to Newton-meters conversion page. Below you can find the code of the home page.

public partial class HomePage : BasePage
{
    public HomePage(IWebDriver driver) : base(driver)
    {
    }

    public override string Url
    {
        get
        {
            return "http://www.metric-conversions.org/";
        }
    }
}
public partial class HomePage
{
    public IWebElement EnergyAndPowerAnchor
    {
        get
        {
            return this.driver.FindElement(By.XPath("//a[contains(@title,'Energy Conversion')]"));
        }
    }

    public IWebElement KilowattHours
    {
        get
        {
            return this.driver.FindElement(By.XPath("//a[contains(text(),'Kilowatt-hours')]"));
        }
    }
}

The Kilowatt-hours to Newton-meters conversion page contains Asserter class too.

public partial class KilowattHoursPage : BasePage
{
    public KilowattHoursPage(IWebDriver driver) : base(driver)
    {
    }

    public override string Url
    {
        get
        {
            return "http://www.metric-conversions.org/temperature/celsius-to-fahrenheit.htm";
        }
    }

    public void ConvertKilowattHoursToNewtonMeters(double kWh)
    {
        this.CelsiusInput.SendKeys(kWh.ToString());
        this.driverWait.Until(drv => this.Answer != null);
    }
}

Except for the URL, the another important part is the convert method that types the kilowatt-hours to the input and waits for the answer field to show up.

public partial class KilowattHoursPage
{
    public IWebElement CelsiusInput
    {
        get
        {
            return this.driver.FindElement(By.Id("argumentConv"));
        }
    }

    public IWebElement Answer
    {
        get
        {
            return this.driver.FindElement(By.Id("answer"));
        }
    }
        
    public IWebElement KilowatHoursToNewtonMetersAnchor
    {
        get
        {
            return this.driver.FindElement(By.XPath("//a[contains(text(),'Kilowatt-hours to Newton-meters')]"));
        }
    }
}

Noting so special about the element map. As a fan of XPath, I find the links by their inner text.

public static class KilowattHoursPageAsserter
{
    public static void AssertFahrenheit(this KilowattHoursPage page, string expectedNewtonMeters)
    {
        Assert.IsTrue(page.Answer.Text.Contains(string.Format("{0}Nm", expectedNewtonMeters)));
    }
}

Generate Bindings

There are a few ways to autogenerate bindings. Once you click insider the specification file and open the context menu, you will find the 'Generate Step Definitions' item.

Generate Regular Expressions in Attributes Bindings

The default option of generation is through regular expressions.  A new file with the following methods will be generated.

[Binding]
public class ConvertMetricsForNuclearScienceStepsRegularExpressions
{
    [When(@"I navigate to Metric Conversions")]
    public void WhenINavigateToMetricConversions_()
    {
        ScenarioContext.Current.Pending();
    }

    [When(@"navigate to Energy and power section")]
    public void WhenNavigateToEnergyAndPowerSection()
    {
        ScenarioContext.Current.Pending();
    }

    [When(@"navigate to Kilowatt-hours")]
    public void WhenNavigateToKilowatt_Hours()
    {
        ScenarioContext.Current.Pending();
    }

    [When(@"choose conversions to Newton-meters")]
    public void WhenChooseConversionsToNewton_Meters()
    {
        ScenarioContext.Current.Pending();
    }

    [When(@"type (.*) kWh")]
    public void WhenTypeKWh(int p0)
    {
        ScenarioContext.Current.Pending();
    }

    [Then(@"assert that (.*) Nm are displayed as answer")]
    public void ThenAssertThatENmAreDisplayedAsAnswer(Decimal p0, int p1)
    {
        ScenarioContext.Current.Pending();
    }
}

The steps are found by SpecFlow through the regex expressions present in the different steps attributes. Also, the class should be marked with the Binding attribute. The (.*) parts of the expressions are injected automatically on test execution to the methods parameters. The automatic generation is not so smart all of the times so sometimes you need to fix the regex expressions manually.

Generate Method Underscores Bindings

The following file will be automatically generated.

[Binding]
public class ConvertMetricsForNuclearScienceStepsMethodUnderscrores
{
    [When]
    public void When_I_navigate_to_Metric_Conversions()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void When_navigate_to_Energy_and_power_section()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void When_navigate_to_Kilowatt_hours()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void When_choose_conversions_to_Newton_meters()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void When_type_P0_kWh(int p0)
    {
        ScenarioContext.Current.Pending();
    }

    [Then]
    public void Then_assert_that_P0_Nm_are_displayed_as_answer(string p1)
    {
        ScenarioContext.Current.Pending();
    }
}

Here the parameters are part of the name (P0, P1...PN). During test execution, they are again injected automatically to the methods parameters . If you want to rename the parameter, you need to change its name to the method's name as well.

Generate Pascal Case Bindings

The following file will be generated.

[Binding]
public class ConvertMetricsForNuclearScienceSteps
{
    [When]
    public void WhenINavigateToMetricConversions()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void WhenNavigateToEnergyAndPowerSection()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void WhenNavigateToKilowattHours()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void WhenChooseConversionsToNewtonMeters()
    {
        ScenarioContext.Current.Pending();
    }

    [When]
    public void WhenType_P0_KWh(int p0)
    {
        ScenarioContext.Current.Pending();
    }

    [Then]
    public void ThenAssertThat_P0_NmAreDisplayedAsAnswer(string p0)
    {
        ScenarioContext.Current.Pending();
    }
}

The file is almost identical to the previous one except the different words are not separated by underscores. Underscores are used only when the method accepts parameters.

Copy Bindings to Clipboard

Once you have the binding file but want to add some new step you can copy its content to the clipboard and add it manually to your file.

Populate Binding Methods

Here is how the binding class will look like using the already created pages.

[Binding]
public class ConvertMetricsForNuclearScienceSteps
{
    private HomePage homePage;
    private KilowattHoursPage kilowattHoursPage;

    [Given(@"web browser is opened")]
    public void GivenWebBrowserIsOpened()
    {
        Driver.StartBrowser(BrowserTypes.Chrome);
    }

    [Then(@"close web browser")]
    public void ThenCloseWebBrowser()
    {
        Driver.StopBrowser();
    }

    [When(@"I navigate to Metric Conversions")]
    public void WhenINavigateToMetricConversions_()
    {
        this.homePage = new HomePage(Driver.Browser);
        this.homePage.Open();
    }

    [When(@"navigate to Energy and power section")]
    public void WhenNavigateToEnergyAndPowerSection()
    {
        this.homePage.EnergyAndPowerAnchor.Click();
    }

    [When(@"navigate to Kilowatt-hours")]
    public void WhenNavigateToKilowatt_Hours()
    {
        this.homePage.KilowattHours.Click();
    }

    [When(@"choose conversions to Newton-meters")]
    public void WhenChooseConversionsToNewton_Meters()
    {
        this.kilowattHoursPage = new KilowattHoursPage(Driver.Browser);
        this.kilowattHoursPage.KilowatHoursToNewtonMetersAnchor.Click();
    }

    [When(@"type (.*) kWh")]
    public void WhenTypeKWh(double kWh)
    {
        this.kilowattHoursPage.ConvertKilowattHoursToNewtonMeters(kWh);
    }

    [Then(@"assert that (.*) Nm are displayed as answer")]
    public void ThenAssertThatENmAreDisplayedAsAnswer(string expectedNewtonMeters)
    {
        this.kilowattHoursPage.AssertFahrenheit(expectedNewtonMeters);
    }
}

Execute SpecFlow Tests

Once we build the project, our test/scenario will show up in the tests explorer.

Test Outcome

The generated output from the test execution is nice. You can observe each executed step and its execution time.

Debug Scenarios

If you need to debug your tests, you have a few options. You can set a break point inside your binding methods. Or instead, you can set on scenario level directly in your feature file.

Specflow Series

The post Getting Started with SpecFlow in 10 Minutes appeared first on Automate The Planet.

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
License Agreement

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.190306.1 | Last Updated 24 Oct 2016
Article Copyright 2016 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid