Advanced SpecFlow: Using Hooks to Run Additional Automation Code

SpecFlow is a tool that allows the writing of business-readable tests that can then be automated in code. If you’re new to SpecFlow check out my Pluralsight course to get up to speed before looking at these more advanced topics.

In addition to executing test automation code in step definitions, SpecFlow provides a number of additional “hooks” to allow additional code execution at various points during the test execution lifecycle.

For this example, consider the following feature:

Feature: SomeFeature

Scenario: Add New Contact Name
    Given I have entered Sarah
    When I choose add
    Then the contact list should show the new contact

With the following step definitions:

using TechTalk.SpecFlow;

namespace SpecFlowHooks
{
    [Binding]
    public class SomeFeatureSteps
    {
        [Given]
        public void Given_I_have_entered_NEWNAME(string newName)
        {
            // etc.
        }
        
        [When]
        public void When_I_choose_add()
        {
            // etc.   
        }
        
        [Then]
        public void Then_the_contact_list_should_show_the_new_contact()
        {
            // etc.
        }
    }
}

When the scenario is executed we get the following test output:

NUnit 1.0.0.0 executing tests is started
Given I have entered Sarah
-> done: SomeFeatureSteps.Given_I_have_entered_NEWNAME("Sarah") (0.0s)
When I choose add
-> done: SomeFeatureSteps.When_I_choose_add() (0.0s)
Then the contact list should show the new contact
-> done: SomeFeatureSteps.Then_the_contact_list_should_show_the_new_contact() (0.0s)
NUnit 1.0.0.0 executing tests is finished

Here we can see the flow of execution, included the Given, When, and Then steps being executed.

Before and After Test Run Hooks

The [BeforeTestRun] and [AfterTestRun] attributes allow the execution of code before and/or after an entire test run occurs. If there are multiple feature files being executed these events still get called only once.

Take note of the warning in the documentation: “As the most of the unit test runners does not provide a hook for executing logic after the test execution has been finished, the [AfterTestRun] event is triggered by the test assembly unload event. The exact timing and thread of this execution might be different for each test runner.

First off we can create a new class, with the [Binding] attribute and create methods with these hooks:

using System;
using TechTalk.SpecFlow;

namespace SpecFlowHooks
{
    [Binding]
    class BeforeAfterTestRun
    {
        [BeforeTestRun]
        public static void Before()
        {
            Console.WriteLine("** [BeforeTestRun]");
        }

        [AfterTestRun]
        public static void After()
        {
            Console.WriteLine("** [AfterTestRun]");
        }
    }
}

When the scenario is executed, we now get the following output:

NUnit 1.0.0.0 executing tests is started
** [BeforeTestRun]
Given I have entered Sarah
-> done: SomeFeatureSteps.Given_I_have_entered_NEWNAME("Sarah") (0.0s)
When I choose add
-> done: SomeFeatureSteps.When_I_choose_add() (0.0s)
Then the contact list should show the new contact
-> done: SomeFeatureSteps.Then_the_contact_list_should_show_the_new_contact() (0.0s)
NUnit 1.0.0.0 executing tests is finished

Notice that we get the ** [BeforeTestRun] executed, but not the * [AfterTestRun] – this comes back to the aforementioned warning.

Before and After Feature Hooks

These hooks execute before and after each feature is executed.

Image a new feature is added:

Feature: SomeOtherFeature

Scenario: Demo
    Given A
    When B
    Then C

So now there are 2 feature files in the solution.

We can add another class to use the [BeforeFeature] and [AfterFeature] hooks:

using System;
using TechTalk.SpecFlow;

namespace SpecFlowHooks
{
    [Binding]
    class BeforeAfterFeature
    {
        [BeforeFeature]
        public static void Before()
        {
            Console.WriteLine("** [BeforeFeature]");
        }

        [AfterFeature]
        public static void After()
        {
            Console.WriteLine("** [AfterFeature]");
        }
    }
}

When all the features (all 2 of them) in the solution are run:

------ Run test started ------
NUnit 1.0.0.0 executing tests is started
** [BeforeTestRun]
** [BeforeFeature]
Given I have entered Sarah
-> done: SomeFeatureSteps.Given_I_have_entered_NEWNAME("Sarah") (0.0s)
When I choose add
-> done: SomeFeatureSteps.When_I_choose_add() (0.0s)
Then the contact list should show the new contact
-> done: SomeFeatureSteps.Then_the_contact_list_should_show_the_new_contact() (0.0s)
** [AfterFeature]
** [BeforeFeature]
Given A
-> done: SomeOtherFeatureSteps.Given_A() (0.0s)
When B
-> done: SomeOtherFeatureSteps.When_B() (0.0s)
Then C
-> done: SomeOtherFeatureSteps.Then_C() (0.0s)
** [AfterFeature]
NUnit 1.0.0.0 executing tests is finished
========== Run test finished: 2 run (0:00:05.7118707) ==========

We now get the ** [BeforeFeature] and ** [AfterFeature] executed twice, once for each feature file.

Before and After Scenario Block Hooks

These hooks execute before and after each logical block in the scenarios. A block is the set of Given, When, and then line(s).

Feature: ScenarioBlockDemo

Scenario: Scenario A
    Given Step A
        And Step B
    When Step C
        And Step D
    Then Step E
        But Step F

The above feature contains 3 logical blocks:

  1. Given Step A … And Step B
  2. When Step C … And Step D
  3. Then Step E … But Step F

The [BeforeScenarioBlock] and [AfterScenarioBlock] hooks execute before and after each of these logical blocks:

NUnit 1.0.0.0 executing tests is started
** [BeforeTestRun]
** [BeforeFeature]
** [BeforeScenarioBlock]
Given Step A
-> done: ScenarioBlockDemoSteps.Given_Step_A() (0.0s)
And Step B
-> done: ScenarioBlockDemoSteps.Given_Step_B() (0.0s)
** [AfterScenarioBlock]
** [BeforeScenarioBlock]
When Step C
-> done: ScenarioBlockDemoSteps.When_Step_C() (0.0s)
And Step D
-> done: ScenarioBlockDemoSteps.When_Step_D() (0.0s)
** [AfterScenarioBlock]
** [BeforeScenarioBlock]
Then Step E
-> done: ScenarioBlockDemoSteps.Then_Step_E() (0.0s)
But Step F
-> done: ScenarioBlockDemoSteps.Then_Step_F() (0.0s)
** [AfterScenarioBlock]
** [AfterFeature]
NUnit 1.0.0.0 executing tests is finished

Other Hooks

There are also [BeforeStep] and [AfterStep] hooks that execute before/after each individual steps in scenarios.

The [BeforeScenario] and [AfterScenario] hooks execute before/after each scenario executes. From SpecFlow 1.8 shorter versions of these are: [Before] and [After] – though the longer versions are a little more descriptive.

 

To learn more about Hooks and Scoped Bindings, check out my SpecFlow Tips and Tricks Pluralsight course.

SHARE:

Add comment

Loading