Mixing Specflow with Behaviours Design Pattern

Mixing Specflow with Behaviours Design Pattern

Use Case

The primary goal of the below tests is going to be to purchase different items from the online store. They need to make sure that the correct pric

1. Navigate to Item’s Page

Ebay Item Page

2. Validate the price

3. Click Buy It Now button

Click Continue as a guest

Fill Shipping Info Online Store

Checkout Page Ebay Validate Total Price

Behaviours’ Based Tests

Behaviours

public class ItemPageNavigationBehaviour : ActionBehaviour
{
    private readonly ItemPage itemPage;
    private readonly string itemUrl;
    public ItemPageNavigationBehaviour(string itemUrl)
    {
        this.itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
        this.itemUrl = itemUrl;
    }
    protected override void PerformAct()
    {
        this.itemPage.Navigate(this.itemUrl);
    }
}
public class ShippingAddressPageFillShippingBehaviour : ActionBehaviour
{
    private readonly ShippingAddressPage shippingAddressPage;
    private readonly ClientPurchaseInfo clientPurchaseInfo;
    public ShippingAddressPageFillShippingBehaviour(
    ClientPurchaseInfo clientPurchaseInfo)
    {
        this.shippingAddressPage =
        UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        this.clientPurchaseInfo = clientPurchaseInfo;
    }
    protected override void PerformAct()
    {
        this.shippingAddressPage.FillShippingInfo(this.clientPurchaseInfo);
    }
}

Test Using Behaviours’ Execution Engine

The behaviours are added as a list to a special behaviours executor that is responsible for executing them in the appropriate way. If the behaviour depends on any data, it is passed to its constructor.


public void Purchase_SimpleBehaviourEngine()
{
    var itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
    var itemPrice = "40.49";
    var clientPurchaseInfo = new ClientPurchaseInfo(
    new ClientAddressInfo()
    {
        FullName = "John Smith",
        Country = "United States",
        Address1 = "950 Avenue of the Americas",
        State = "New York",
        City = "New York City",
        Zip = "10001-2121",
        Phone = "00164644885569"
    });
    clientPurchaseInfo.CouponCode = "99PERDIS";
    var clientLoginInfo = new ClientLoginInfo()
    {
        Email = "g3984159@trbvm.com",
        Password = "ASDFG_12345"
    };
    PerfectSystemTestsDesign.Behaviours.Core.BehaviourExecutor.Execute(
    new ItemPageNavigationBehaviour(itemUrl),
    new ItemPageBuyBehaviour(),
    new PreviewShoppingCartPageProceedBehaviour(),
    new SignInPageLoginBehaviour(clientLoginInfo),
    new ShippingAddressPageFillShippingBehaviour(clientPurchaseInfo),
    new ShippingAddressPageFillDifferentBillingBehaviour(clientPurchaseInfo),
    new ShippingAddressPageContinueBehaviour(),
    new ShippingPaymentPageContinueBehaviour(),
    new PlaceOrderPageAssertFinalAmountsBehaviour(itemPrice));
}

SpecFlow Test

Feature File

Feature: Create Purchase in Online Store
In order to receive a book online
As a client
I want to be able to choose it through the browser and pay for it online
@testingFramework
Scenario: Create Successfull Purchase When Billing Country Is United States with American Express Card
When I navigate to "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743"
And I click the 'buy now' button
And then I click the 'proceed to checkout' button
#When the login page loads
And I login with email = "g3984159@trbvm.com" and pass = "ASDFG_12345"
#When the shipping address page loads
And I type full name = "John Smith", country = "United States", Adress = "950 Avenue of the Americas", city = "New Your City", state = "New Your", zip = "10001-2121" and phone = "00164644885569"
And I choose to fill different billing, full name = "John Smith", country = "United States", Adress = "950 Avenue of the Americas", city = "New Your City", state = "New Your", zip = "10001-2121" and phone = "00164644885569"
And click shipping address page 'continue' button
And click shipping payment top 'continue' button
Then assert that order total price = "40.49"

SpecFlow Bindings Using Page Objects


public class CreatePurchaseSteps
{
    [When(@"I navigate to ""([^""]*)""")]
    public void NavigateToItemUrl(string itemUrl)
    {
        var itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
        itemPage.Navigate(itemUrl);
    }
    [When(@"I click the 'buy now' button")]
    public void ClickBuyNowButtonItemPage()
    {
        var itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
        itemPage.ClickBuyNowButton();
    }
    [When(@"then I click the 'proceed to checkout' button")]
    public void ClickProceedToCheckoutButtonPreviewShoppingCartPage()
    {
        var previewShoppingCartPage = UnityContainerFactory.GetContainer().Resolve<PreviewShoppingCartPage>();
        previewShoppingCartPage.ClickProceedToCheckoutButton();
    }
    [When(@"the login page loads")]
    public void SignInPageLoads()
    {
        var signInPage = UnityContainerFactory.GetContainer().Resolve<SignInPage>();
        signInPage.WaitForPageToLoad();
    }
    [When(@"I login with email = ""([^""]*)"" and pass = ""([^""]*)""")]
    public void LoginWithEmailAndPass(string email, string password)
    {
        var signInPage = UnityContainerFactory.GetContainer().Resolve<SignInPage>();
        signInPage.Login(email, password);
    }
    [When(@"the shipping address page loads")]
    public void ShippingPageLoads()
    {
        var shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        shippingAddressPage.WaitForPageToLoad();
    }
    [When(@"I type full name = ""([^""]*)"", country = ""([^""]*)"", Adress = ""([^""]*)"", city = ""([^""]*)"", state = ""([^""]*)"", zip = ""([^""]*)"" and phone = ""([^""]*)""")]
    public void FillShippingInfo(string fullName, string country, string address, string state, string city, string zip, string phone)
    {
        var shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        var clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = fullName,
            Country = country,
            Address1 = address,
            State = state,
            City = city,
            Zip = zip,
            Phone = phone
        });
        shippingAddressPage.FillShippingInfo(clientPurchaseInfo);
    }
    [When(@"I choose to fill different billing, full name = ""([^""]*)"", country = ""([^""]*)"", Adress = ""([^""]*)"", city = ""([^""]*)"", state = ""([^""]*)"", zip = ""([^""]*)"" and phone = ""([^""]*)""")]
    public void FillDifferentBillingInfo(string fullName, string country, string address, string state, string city, string zip, string phone)
    {
        var shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        var shippingPaymentPage = UnityContainerFactory.GetContainer().Resolve<ShippingPaymentPage>();
        var clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = fullName,
            Country = country,
            Address1 = address,
            State = state,
            City = city,
            Zip = zip,
            Phone = phone
        });
        shippingAddressPage.ClickDifferentBillingCheckBox(clientPurchaseInfo);
        shippingAddressPage.ClickContinueButton();
        shippingPaymentPage.ClickBottomContinueButton();
        shippingAddressPage.FillBillingInfo(clientPurchaseInfo);
    }
    [When(@"click shipping address page 'continue' button")]
    public void ClickContinueButtonShippingAddressPage()
    {
        var shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        shippingAddressPage.ClickContinueButton();
    }
    [When(@"click shipping payment top 'continue' button")]
    public void WhenClickTopPaymentButton()
    {
        var shippingPaymentPage = UnityContainerFactory.GetContainer().Resolve<ShippingPaymentPage>();
        shippingPaymentPage.ClickTopContinueButton();
    }
    [Then(@"assert that order total price = ""([^""]*)""")]
    public void AssertOrderTotalPrice(string itemPrice)
    {
        var placeOrderPage = UnityContainerFactory.GetContainer().Resolve<PlaceOrderPage>();
        double totalPrice = double.Parse(itemPrice);
        placeOrderPage.AssertOrderTotalPrice(totalPrice);
    }
}

SpecFlow Bindings- Direct Usage


public class CreatePurchaseStepsBehaviours
{
    [When(@"I navigate to ""([^""]*)""")]
    public void NavigateToItemUrl(string itemUrl)
    {
        new ItemPageNavigationBehaviour(itemUrl).Execute();
    }
    [When(@"I click the 'buy now' button")]
    public void ClickBuyNowButtonItemPage()
    {
        new ItemPageBuyBehaviour().Execute();
    }
    [When(@"then I click the 'proceed to checkout' button")]
    public void ClickProceedToCheckoutButtonPreviewShoppingCartPage()
    {
        new PreviewShoppingCartPageProceedBehaviour().Execute();
    }
    [When(@"I login with email = ""([^""]*)"" and pass = ""([^""]*)""")]
    public void LoginWithEmailAndPass(string email, string password)
    {
        new SignInPageLoginBehaviour(
        new ClientLoginInfo()
        {
            Email = email,
            Password = password
        })
        .Execute();
    }
    [When(@"the shipping address page loads")]
    public void ShippingPageLoads()
    {
        var shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
        shippingAddressPage.WaitForPageToLoad();
    }
    [When(@"I type full name = ""([^""]*)"", country = ""([^""]*)"", Adress = ""([^""]*)"", city = ""([^""]*)"", state = ""([^""]*)"", zip = ""([^""]*)"" and phone = ""([^""]*)""")]
    public void FillShippingInfo(string fullName, string country, string address, string state, string city, string zip, string phone)
    {
        var clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = fullName,
            Country = country,
            Address1 = address,
            State = state,
            City = city,
            Zip = zip,
            Phone = phone
        });
        new ShippingAddressPageFillShippingBehaviour(clientPurchaseInfo).Execute();
    }
    [When(@"I choose to fill different billing, full name = ""([^""]*)"", country = ""([^""]*)"", Adress = ""([^""]*)"", city = ""([^""]*)"", state = ""([^""]*)"", zip = ""([^""]*)"" and phone = ""([^""]*)""")]
    public void FillDifferentBillingInfo(string fullName, string country, string address, string state, string city, string zip, string phone)
    {
        var clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = fullName,
            Country = country,
            Address1 = address,
            State = state,
            City = city,
            Zip = zip,
            Phone = phone
        });
        new ShippingAddressPageFillDifferentBillingBehaviour(clientPurchaseInfo).Execute();
    }
    [When(@"click shipping address page 'continue' button")]
    public void ClickContinueButtonShippingAddressPage()
    {
        new ShippingPaymentPageContinueBehaviour().Execute();
    }
    [When(@"click shipping payment top 'continue' button")]
    public void WhenClickTopPaymentButton()
    {
        new ShippingPaymentPageContinueBehaviour().Execute();
    }
    [Then(@"assert that order total price = ""([^""]*)""")]
    public void AssertOrderTotalPrice(string itemPrice)
    {
        new PlaceOrderPageAssertFinalAmountsBehaviour(itemPrice).Execute();
    }
}
public abstract class ActionBehaviour
{
    public virtual void Execute()
    {
        this.PerformAct();
    }
    protected abstract void PerformAct();
}

SpecFlow Bindings- Directly in Behaviours


public class ItemPageNavigationBehaviour : ActionBehaviour
{
    private readonly ItemPage itemPage;
    private string itemUrl;
    public ItemPageNavigationBehaviour()
    {
        this.itemPage = UnityContainerFactory.GetContainer().Resolve<ItemPage>();
    }
    [When(@"I navigate to ""([^""]*)""")]
    public void NavigateToItemUrl(string itemUrl)
    {
        this.itemUrl = itemUrl;
        base.Execute();
    }
    protected override void PerformAct()
    {
        this.itemPage.Navigate(this.itemUrl);
    }
}

public class ShippingAddressPageFillShippingBehaviour : ActionBehaviour
{
    private readonly ShippingAddressPage shippingAddressPage;
    private ClientPurchaseInfo clientPurchaseInfo;
    public ShippingAddressPageFillShippingBehaviour()
    {
        this.shippingAddressPage = UnityContainerFactory.GetContainer().Resolve<ShippingAddressPage>();
    }
    [When(@"I type full name = ""([^""]*)"", country = ""([^""]*)"", Adress = ""([^""]*)"", city = ""([^""]*)"", state = ""([^""]*)"", zip = ""([^""]*)"" and phone = ""([^""]*)""")]
    public void FillShippingInfo(string fullName, string country, string address, string state, string city, string zip, string phone)
    {
        this.clientPurchaseInfo = new ClientPurchaseInfo(
        new ClientAddressInfo()
        {
            FullName = fullName,
            Country = country,
            Address1 = address,
            State = state,
            City = city,
            Zip = zip,
            Phone = phone
        });
        shippingAddressPage.FillShippingInfo(clientPurchaseInfo);
        base.Execute();
    }
    protected override void PerformAct()
    {
        this.shippingAddressPage.FillShippingInfo(this.clientPurchaseInfo);
    }
}

Related Articles

Web Automation

10 Advanced WebDriver Tips and Tricks Part 1

As you probably know I am developing a series posts called- Pragmatic Automation with WebDriver. They consist of tons of practical information how to start writ

10 Advanced WebDriver Tips and Tricks Part 1

AutomationTools, Free Tools, Web Automation

UI Performance Analysis via Selenium WebDriver

The article from the series Automation Tools reviews different approaches to check the UI performance of web apps reusing your existing functional Selenium WebD

UI Performance Analysis via Selenium WebDriver

Web Automation

Design Grid Control Automated Tests Part 3

In my previous articles Design Grid Control Automated Tests Part 1 and Design Grid Control Automated Tests Part 2 I started a mini-series about writing decent g

Design Grid Control Automated Tests Part 3

Web Automation

Selenium C# xUnit Test Automating Angular, React, VueJS and 20 More

In the new article from the Web Automation Series with C#, we will talk about creating a data-driven xUnit test automating all major web technologies such as Re

Selenium C# xUnit Test Automating Angular, React, VueJS and 20 More

Web Automation

Selenium C# MSTest Test Automating Angular, React, VueJS and 20 More

In the new article from the Web Automation Series with C#, we will talk about creating a data-driven MSTest test automating all major web technologies such as R

Selenium C# MSTest Test Automating Angular, React, VueJS and 20 More

Specflow, Web Automation

Advanced SpecFlow: Using Hooks to Extend Test Execution Workflow

Last week I announced a new series of articles dedicated to Specflow (Behavior Driven Development for .NET). In my first publication, I showed you how to create

Advanced SpecFlow: Using Hooks to Extend Test Execution Workflow
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.