Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Advanced SpecFlow: 4 Ways for Handling Parameters Properly

4.43/5 (3 votes)
14 Nov 2016Ms-PL4 min read 18K  
Learn how to handle more sophisticated scenarios that require multiple parameters. Create data driven tests using scenario outline examples tables.

  

Introduction

From the articles part of the Specflow Series, you can learn how to write acceptance tests following the BDD paradigm so that your tests are understandable to business users. In this publication, I am going to share with you four ways for handling parameters in the feature files.

1. Create Steps with Optional Parameters

Use Case

Convert Kilowatt Hours to Newton Meters Different Format

In the previous examples, we wrote tests that converted kilowatt-hours to newton-meters. The step that types the kWh is the following one- "And type "30" kWh". In the converter application, there is a new feature - to change the format of the answer and we need to test it. We want to be able to extend the previous step with a parameter for the chosen format - "And type 30 kWh in Fractions format". We want to support both steps.

Feature Scenarios

Scenario: Successfully Convert Kilowatt-hours to Newton-meters
	When I navigate to Metric Conversions
	And navigate to Energy and power section
	And navigate to Kilowatt-hours
	And choose conversions to Newton-meters
	And type "30" kWh
	Then assert that 1.080000e+8 Nm are displayed as answer

Scenario: Successfully Convert Kilowatt-hours to Newton-meters in Fractions format
	When I navigate to Metric Conversions
	And navigate to Energy and power section
	And navigate to Kilowatt-hours
	And choose conversions to Newton-meters
	And type 30 kWh in Fractions format
	Then assert that 1079999999⁄64 Nm are displayed as answer

The tests are almost identical with the small differences in the Type step.

Binding Methods

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

[When(@"type (.*) kWh in (.*) format")]
public void WhenTypeKWhInFormat(double kWh, Format format)
{
    this.kilowattHoursPage.ConvertKilowattHoursToNewtonMeters(kWh, format);
}

To be able to support both steps, you need to create two separate binding methods. The first one will accept one parameter and the second one two.

C#
public void ConvertKilowattHoursToNewtonMeters(
    double kWh,
    Format format = CelsiusFahrenheitPage.Format.Decimal)
{
    this.CelsiusInput.SendKeys(kWh.ToString());
    if (format != CelsiusFahrenheitPage.Format.Decimal)
    {
        string formatText =
            Enum.GetName(typeof(CelsiusFahrenheitPage.Format), format);
        new SelectElement(this.Format).SelectByText(formatText);
    }
    this.driverWait.Until(drv => this.Answer != null);
}

The first binding method will call the new method without the optional parameter. If we choose Fractions format, the method will choose it from the drop down and convert the answer.

2. Optional Parameters through ArgumentTransformation

Use Case

Convert Seconds to Minutes

Here, we want to convert seconds to minutes. However, we want to support inputs like the below:

  • 1 day, 1 hour, 1 minute, 1 second
  • 4 hours, 3 minutes, 2 seconds
  • 5 days, 3 minutes
  • 3 minutes, 2 seconds
  • 4 hours

The step should convert the input in seconds and then type them.

Feature Scenarios

This is how the scenarios will look like. As you can see, we pass different combinations of days, hours, minutes and seconds.

Scenario: Successfully Convert Seconds to Minutes
	When I navigate to Seconds to Minutes Page
	And type seconds for 1 day, 1 hour, 1 minute, 1 second
	Then assert that 1501 minutes are displayed as answer

Scenario: Successfully Convert Seconds to Minutes No Minutes
	When I navigate to Seconds to Minutes Page
	And type seconds for 1 day, 1 hour, 1 second
	Then assert that 1500 minutes are displayed as answer

Binding Methods

As you can see, the type seconds binding doesn't contain anything special about these inputs. All of the magic is happening in the StepArgumentTransformation step which has its custom regex pattern. Basically, it translates only the part of the date time inputs to TimeSpan which we, later on, convert to seconds. I am not going to decipher the regex since this is not the main topic of the article. You can find more information in the official SpecFlow documentation.

C#
[When(@"type seconds for (.*)")]
public void WhenTypeSeconds(TimeSpan seconds)
{
    this.secondsToMinutesPage.ConvertSecondsToMintes(seconds.TotalSeconds);
}

[Then(@"assert that (.*) minutes are displayed as answer")]
public void ThenAssertThatSecondsAreDisplayedAsAnswer(int expectedMinutes)
{
    this.secondsToMinutesPage.AssertMinutes(expectedMinutes.ToString());
}

[StepArgumentTransformation(@"(?:(\d*) day(?:s)?(?:, )?)?(?:(\d*) hour(?:s)?(?:, )?)?
(?:(\d*) minute(?:s)?(?:, )?)?(?:(\d*) second(?:s)?(?:, )?)?")]
public TimeSpan TimeSpanTransform(string days, string hours, string minutes, string seconds)
{
    int daysParsed;
    int hoursParsed;
    int minutesParsed;
    int secondsParsed;

    int.TryParse(days, out daysParsed);
    int.TryParse(hours, out hoursParsed);
    int.TryParse(minutes, out minutesParsed);
    int.TryParse(seconds, out secondsParsed);

    return new TimeSpan(daysParsed, hoursParsed, minutesParsed, secondsParsed);
}

3. Data Driven Tests- Examples Table

Use Case

Generated Data Driven Tests Specflow

Instead of copy pasting the scenarios, we want to specify seconds' inputs and the expected results and run the tests for all specified data. Above, we have generated tests for four data sets.

Feature Scenarios

We can use the scenario outline examples table to accomplish the use case. First, instead of using Scenario: we need to change it to Scenario Outline: Below the steps, we specify all of the data sets in the Examples table. You mark the start of the table through Examples: row, then the first row of the table contains the parameters names. Then you can use these names in the scenario using the following syntax <yourparameter>.

Scenario Outline: Successfully Convert Seconds to Minutes Table
	When I navigate to Seconds to Minutes Page
	And type seconds for <seconds>
	Then assert that <minutes> minutes are displayed as answer
Examples:
| seconds						            | minutes   | 
| 1 day, 1 hour, 1 second       | 1500	   	| 
| 5 days, 3 minutes 			      | 7203		  | 
| 4 hours					            	| 240		    | 
| 180 seconds     				      | 3			    | 

Format Data Table

After that, you write the data separated with | symbol. If you type the delimiters manually, Visual Studio will format the table for you, but if you paste the data, you will have to do it manually.

Test Names

For each row, Specflow will generate a separate test. Keep in mind that the names are based on the first parameter of the table (you can see that in the use case image). So if the data in your first column is not unique, your tests' names will be generated using numbers. In this case, you can add a new first column specifying the test name's suffix yourself.

4. Pass List of Object to Step

Use Case

Add Product with Affiliate Code Amazon

You want in a single test to add multiple items to your shopping cart containing affiliate codes. We need to pass a list of pairs of URLs and affiliate codes to our step.

Feature Scenarios

Scenario: Add Amazon Products with Affiliate Codes
	When add products
	| Url                                      | AffilicateCode |
	| /dp/B00TSUGXKE/ref=ods_gw_d_h1_tab_fd_c3 | affiliate3     |
	| /dp/B00KC6I06S/ref=fs_ods_fs_tab_al      | affiliate4     |
	| /dp/B0189XYY0Q/ref=fs_ods_fs_tab_ts      | affiliate5     |
	| /dp/B018Y22C2Y/ref=fs_ods_fs_tab_fk      | affiliate6     |

You can pass a table of parameters with the above syntax. The test will open each of the URLs adding the specified affiliate code as a query parameter to the URL and then click the Buy Now button.

Binding Methods

You need to install the SpecFlow.Assist.Dynamic NuGet and add a using statement to TechTalk.SpecFlow.Assist. Your step should accept a parameter of type Table. You can iterate through the items of the table without the help of the classes part of the mentioned NuGet. However, I don't think this is a good practice. I believe that this approach is cleaner. After you pass the table, you use the CreateDynamicSet extension method which returns a collection of dynamic objects. You can access the different columns of the table from the returned objects as properties, but you need to be sure that you don't have any typos since in this mode, Visual Studio is not going to warn you. Here, you can read more about the dynamic type.

C#
[When(@"add products")]
public void NavigateToItemUrl(Table productsTable)
{
    var itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
    IEnumerable<dynamic> products = productsTable.CreateDynamicSet();
    foreach (var product in products)
    {
        itemPage.Navigate(string.Concat(product.Url, "?", product.AffilicateCode));
        itemPage.ClickBuyNowButton();
    }
}

Specflow Series

References

The post Advanced SpecFlow: 4 Ways for Handling Parameters Properly appeared first on Automate The Planet.

License

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