Getting Started with SpecFlow in 10 Minutes

Specflow
1157 Shares
Getting Started with SpecFlow

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)

Install SpecFlow Extension Visual Studio

2. Install SpecFlow NuGet

Install Specflow NuGet

3. Install SpecFlow for MSTest NuGet

Install SpecFlow Unit Test Framework

4. Add Feature File

Add New Item Project
SpecFlow Feature File

5. Create Your Scenarios

Not Bind Steps SpecFlow

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.

Metric Conversions Home Page

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

Kilowatt-hours to Newton Conversion Page

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.

Not Bind Steps SpecFlow

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 SpecFlow Step Definitions

Generate Regular Expressions in Attributes Bindings

Generate Step Definitions Window Regular Expressions

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

Generate Step Definitions Skeleton Window Method Name Underscores

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

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

Copy Methods 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.

Generated Specflow Test

Test Outcome

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

Test Outcome Output SpecFlow

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.

Breakpoint Feature File
  • Tom

    Thank you for an interesting read. However, I am finding this one pretty confusing, which due to certain omissions and would like you to confirm some ruminations that auto-generated in my mind while following this article or dismiss them as irrelevant && correct me if I’m wrong.

    Here’s what I found would make other readers of this piece feel they have a more immersive and comprehensive experience while reading it and trying to try their hands at following the guide on their own, with the assumption this guide was (or might have been) intended for relative beginners / newcomers to SpecFlow (like me – I believe following the guide and typing the code provided is a lot more effective than just reading it in a forked repo or otherwise downloaded):

    – instructing the reader to create a new project – by comparing icons in your screenshots and my VS IDE I guess the project in this case is a unit test project – this is important, because NuGet installs packages per project (this much or little I know) and otherwise will complain,

    – presenting the project structure with info about packages (folders?) and the classes they should include – I guess it should be easy for anyone to recreate it in their IDE just by studying the one you provided. From my investigation of your github repo for this guide and the slides you provided in this guide I figured out that the Project includes the following packages: Base (with BasePage and UnityContainerFactory classes); Core (with BrowserTypes and Driver classes); Pages, with two subpackages – CelsiusFahrenheitPage (with the classes KilowattHoursPage.Map, KilowattHoursPage, and KilowattHoursPageAsserter) and HomePage (with the classes HomePage.Map and HomePage); a SpecFlow package (as seen in your slides), and other files, but the slides do not show what the package should include nor where the other files (steps, etc.) should be located,

    – providing information where exactly to add feature files and other user-created ones (but that should be clear from the project structure),

    – providing information about “ScenarioContext.Current.Pending();” and why it appears (yes, it’s possible to Google the answer and the SpecFlow documentation has the answer, but it would be so much nicer to have read about it in this intro to SpecFlow) && what happens to it later.

    Thank you for your time.

    • Thank you, Tom, for the great feedback. It is much appreciated! All of the suggestions make perfect sense so I will try to add them as soon as I have free time!

  • Power Donchev

    Awesome!
    I am a little bit disappointed, that something I worked on is already done (I was torturing my mind on how can we give the business users and the QA team) writing their own tests and then perhaps developers / QA engineers that can write code can just fill in the code in the new methods generated by the engine.

    What I was wondering – in my “pseudo engine” – it was more like a facade but I was looking for a way to expand it to more “natural” language like SpecFlow does – you have given, when and then inheriting the same facade. This allows you to reuse a method in given, when and then.

    In SpecFlow (at least It seems to me), that you will be forced to have 2 methods with the same code if you want to use them in given and when. For example, can I have
    [Given(“Create admin user”)]
    [When(“Create admin user”)]
    CreateAdminUser()

    Or I should have something like:

    [Given(“Create admin user”)]
    CreateAdminUserGiven()

    and
    [When(“Create admin user”)]
    CreateAdminUserWhen()

    The other interesting part is – whether this can handle some synonyms (of course, they will be predefined).
    Like – can it handle “Create admin user” and “An admin user is created”?

    But it looks really, really, really, promising technology.

    • Hi Pavel,

      Sorry for my late reply but it was a busy week for me.
      I am glad that you found the article useful. I don’t like to write getting started guides it was just a prerequisite for the more advanced stuff. Yesterday, I published the second post from the series dedicated to SpecFlow hooks.

      I have just tested your two suggestions- both works.

      [Given(@”I go to Metric Conversions”)]
      [Given(@”I navigate to Metric Conversions”)]
      [When(@”I navigate to Metric Conversions”)]
      public void WhenINavigateToMetricConversions_()
      {
      this.homePage.Open();
      }

      The following is fully supported. Here is how looks the feature file.

      Given I go to Metric Conversions
      And I navigate to Metric Conversions
      When I navigate to Metric Conversions

      My test navigates three times to the page.

      By the way, probably you don’t know, but I work on the project that you worked a long time ago in Telerik (Admin, YA and stuff). I started to work for Telerik a couple of months after you left, so I heard lots of stories about you. ;)

      Best


banner