Automate Web Testing in C#: A Guide with PuppeteerSharp and SpecFlow

mukarramjavid

Mukarram Javid

Posted on November 9, 2024

Automate Web Testing in C#: A Guide with PuppeteerSharp and SpecFlow

Learn how to efficiently automate and test web applications in C# using PuppeteerSharp and SpecFlow for streamlined end-to-end testing.

This article may be longer than expected, but I hope you enjoy it.

We often need to automate tasks or test applications as developers or testers. In such cases, there is usually limited time for manual testing, making PuppeteerSharp an ideal tool for task automation.

Before starting, we need to install two NuGet packages by running the following commands in the Package Manager Console window.

Install-Package SpecFlow.NUnit
Install-Package PuppeteerSharp
Enter fullscreen mode Exit fullscreen mode

I have discussed Puppeteer in a previous blog you can read it by clicking here.

I’ll have a small discussion regarding SpecFlow.

SpecFlow is a .NET tool used to support Behavior-Driven Development (BDD) by enabling you to write tests in plain language that are easy to read and understand. SpecFlow allows developers and non-technical stakeholders to collaborate on defining application behavior in terms of scenarios written in Gherkin syntax (Given-When-Then format)

Key Features of SpecFlow

  • Gherkin Syntax: Write test scenarios in plain English, making them accessible to both technical and non-technical team members. Integration with .NET: SpecFlow is designed specifically for the .NET ecosystem, enabling seamless integration with tools like NUnit, MSTest, and xUnit.
  • Automated Tests: Scenarios written in SpecFlow can be automatically converted into executable test cases, which can be run to verify application behavior.
  • BDD Workflow: Facilitates a BDD approach, where teams define requirements through examples, making tests serve as both documentation and a verification tool.

By combining SpecFlow with tools like PuppeteerSharp, you can create powerful, readable automation tests for web applications, streamlining both development and testing.

Let’s dive into the coding part.

In this article, I will use Visual Studio for development. we might need to install an extension of SpecFlow in Visual Studio. You can either install it from VS 2022 or manually. It is totally upto you. I assume you have installed this extension.

We create a NUnit project for this.

nunit-project

We do not need UnitTest1.cs file. So we can delete this. Our project will look like this.

nunit-project-2

Now create some folders to manage our code.

  • Utilities (Helpers will be placed in the Utilities folder)
  • Features (All Feature files will be stored in the Features folder)
  • Steps (Step definitions will be organized in the Steps folder)

TestScriptsView

For example, we want to do GoogleSearch.

Here will be our possible steps to do that search

  • Go to Google.com
  • Write some text i.e PuppeteerSharp in the search bar
  • Press Enter
  • Match the search result with provided string.

To define steps we need .feature file. we will create the file in Features folder. “GoogleSearch.feature”. When we add New Item, we will see the SpecFlow section (if you installed SpecFlow extension) in left side of window.

feature-file-solution

When we click on this section. we will see related items of SpecFlow. We need Feature File for SpecFlow. Select it and named it and click on Add button

Image description

After adding the feature file. Let’s breakdown of the contents of a Feature file:

1. Feature
The Feature keyword describes a high-level functionality or behavior of the application.

Example:

Feature: Google Search Functionality
  As a user
  I want to search for text on Google
  So that I can verify the search results match the expected output
Enter fullscreen mode Exit fullscreen mode

In order to: The goal or purpose of the feature.
As a: The role of the person interacting with the feature.
I want to: The actual action or behavior expected from the feature.

2. Scenario
Each Scenario describes a test case with a clear purpose. In this case, the scenario is to search for the term “PuppeteerSharp” on Google and verify that the results contain the expected string.

Scenario: Search for PuppeteerSharp on Google
Enter fullscreen mode Exit fullscreen mode

3. Given, When, Then Steps
The Given, When, Then structure describes the flow of the scenario. Let’s map each of your steps to these terms.

  • Given: Describes the initial state (here, that the user is on Google’s homepage).
  • When: Describes actions taken by the user (entering text and pressing Enter).
  • Then: Describes the expected outcome (the search result should match the expected text).

Here’s how your scenario would look:

  Given I am on Google.com
  When I enter "PuppeteerSharp" in the search bar
  And I press Enter
  Then the search results should contain the text "PuppeteerSharp"
Enter fullscreen mode Exit fullscreen mode

Full Feature File Example
Combining the elements above, here’s the full Feature file:

# GoogleSearch.feature

Feature: Google Search Functionality
  As a user
  I want to search for text on Google
  So that I can verify the search results match the expected output

  Scenario: Search for PuppeteerSharp on Google
    Given I am on Google.com
    When I enter "PuppeteerSharp" in the search bar
    And I press Enter
    Then the search results should contain the text "PuppeteerSharp"
Enter fullscreen mode Exit fullscreen mode

In SpecFlow, we will create methods for each step in a Step Definition file, which links the steps in your Feature file to the actual code that performs those actions. Each method corresponds to a specific step and uses attributes like [Given], [When], and [Then] to associate with the Gherkin steps.

Here’s an example Step Definition file for your steps:

[Binding]
 public class GoogleSearchSteps
 {
     [Given(@"I am on Google.com")]
     public async Task GivenIAmOnGoogleCom()
     {
     }

     [When(@"I enter ""(.*)"" in the search bar")]
     public async Task WhenIEnterTextInTheSearchBar(string searchText)
     {
     }

     [When(@"I press Enter")]
     public async Task WhenIPressEnter()
     {
     }

     [Then(@"the search results should contain the text ""(.*)""")]
     public async Task ThenTheSearchResultsShouldContainTheText(string expectedText)
     {
     }
 }
Enter fullscreen mode Exit fullscreen mode

To initialize the browser, we will create a file named BrowserHelper.cs in the Utilities folder and have the GoogleSearchSteps class inherit from it. In BrowserHelper.cs we initialize the browser using Puppeteer and GoogleSearchSteps inherit from it

BrowserHelper will look like this:

  // BrowserHelper.cs

    public class BrowserHelper
    {
        public static IBrowser _browser;
        public static IPage _page;
        public async Task Init()
        {
            var revision = await new BrowserFetcher(SupportedBrowser.Chromium).DownloadAsync();

            _browser = await Puppeteer.LaunchAsync(new LaunchOptions()
            {
                Headless = false,
                ExecutablePath = revision.GetExecutablePath()
            });

            _page = await _browser.NewPageAsync();
        }
    }
Enter fullscreen mode Exit fullscreen mode
// GoogleSearchSteps

[Binding]
public class GoogleSearchSteps : BrowserHelper
{
}
Enter fullscreen mode Exit fullscreen mode

One more class we might need named Hook.

In SpecFlow, the Hook class is used to execute specific code before or after certain events during test execution. Hooks provide a way to set up and tear down resources, prepare data, and perform cleanup tasks.

1. [Binding] Attribute

  • Marks the Hooks class as a SpecFlow binding class, allowing SpecFlow to recognize and execute the hooks within this class.
  • Ensures that the Hooks class is loaded and its hooks are applied to each test scenario.

2. Inheritance from BrowserHelper

  • The Hooks class inherits from BrowserHelper, suggesting that BrowserHelper contains methods or properties related to initializing or managing a browser instance.
  • This inheritance likely provides access to browser-related functionality used for automated testing (such as launching or closing a browser).

3. [BeforeScenario] Hook

  • [BeforeScenario] is an attribute provided by SpecFlow that marks a method to be run before each test scenario begins.
  • Purpose: This is often used to set up prerequisites for the test, such as initializing the browser, preparing test data, or configuring the environment.
  • Implementation: In this example, FirstBeforeScenario():
  • Calls the Init() method, which likely sets up a browser session.
  • Init() is assumed to be a method from the BrowserHelper class.
  • The method is asynchronous (async), allowing it to handle asynchronous browser setup processes if needed.
[BeforeScenario]
public async Task FirstBeforeScenario()
{
    await Init(); // Initializes the browser session or other test prerequisites
}
Enter fullscreen mode Exit fullscreen mode

4. [AfterScenario] Hook

  • [AfterScenario] is another attribute provided by SpecFlow, but it is triggered after each test scenario has finished executing.
  • Purpose: Commonly used to clean up resources or perform teardown operations after each test scenario, such as closing the browser, clearing test data, or resetting the environment.
  • Implementation: In this example, AfterScenario():
  • Calls _browser.CloseAsync(), which closes the browser session to free up resources.
  • _browser is likely a browser instance that’s initialized in the - - ---- BrowserHelper class or Init() method.
  • Again, the method is asynchronous, ensuring the browser shutdown operation completes before moving on.
[AfterScenario]
public async Task AfterScenario()
{
    await _browser.CloseAsync(); // Closes the browser session or cleans up resources
}
Enter fullscreen mode Exit fullscreen mode

Hooks.cs will look like this:

// Hooks.cs

[Binding]
public sealed class Hooks : BrowserHelper
{
    [BeforeScenario]
    public async Task FirstBeforeScenario()
    {
       await Init();
    }

    [AfterScenario]
    public async Task AfterScenario()
    {
       await _browser.CloseAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

Let’s rebuild the project to resolve our dependencies. After building, open the Test Explorer to view our tests. The test window will then look like this:

To see Test Explorer;

View > Test Explorer or we can use Short key CRTL + E, T.

Image description

To run this test, right-click on the test SearchForPuppeteerSharpOnGoogle and select 'Run'. You will see the test running. The first time, it may take a bit longer because the compiler needs to download the Chromium browser. Once the browser is downloaded, the tests will begin to run.

If the test has been successfully run, we will see a green icon indicating that the test passed successfully.

You can find the source code from here.

This is done for now. Hopefully, you enjoyed it. Have a nice day. We will see next time and in a different blog.

💖 💪 🙅 🚩
mukarramjavid
Mukarram Javid

Posted on November 9, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related