Hooks¶
Hooks (event bindings) can be used to perform additional automation logic at specific times, such as any setup required prior to executing a scenario. In order to use hooks, you need to add the Binding
attribute to your class:
[Binding]
public class MyClass
{
...
}
Hooks are global, but can be restricted to run only for features or scenarios by defining a scoped binding, which can be filtered with tags. The execution order of hooks for the same type is undefined, unless specified explicitly.
SpecFlow+ Runner Restrictions¶
When running tests in multiple threads with SpecFlow+ Runner, Before and After hooks such as BeforeTestRun
and AfterTestRun
are executed once for each thread. This is a limitation of the current architecture.
If you need to execute specific steps once per test run, rather than once per thread, you can do this using deployment transformations. An example can be found here.
Supported Hook Attributes¶
Attribute | Tag filtering* | Description |
---|---|---|
[BeforeTestRun]/[AfterTestRun] | not possible | Automation logic that has to run before/after the entire test run. Note: As most of the unit test runners do not provide a hook for executing logic once the tests have been executed, the [AfterTestRun] event is triggered by the test assembly unload event. The exact timing and thread of this execution may therefore differ for each test runner. The method it is applied to must be static. |
[BeforeFeature]/[AfterFeature] | possible | Automation logic that has to run before/after executing each feature The method it is applied to must be static. |
[BeforeScenario] or [Before] [AfterScenario] or [After] |
possible | Automation logic that has to run before/after executing each scenario or scenario outline example |
[BeforeScenarioBlock] [AfterScenarioBlock] |
possible | Automation logic that has to run before/after executing each scenario block (e.g. between the "givens" and the "whens") |
[BeforeStep] [AfterStep] |
possible | Automation logic that has to run before/after executing each scenario step |
You can annotate a single method with multiple attributes.
Using Hooks with Constructor Injection¶
You can use context injection to access scenario level dependencies in your hook class using constructor injection.
For example you can get the ScenarioContext
injected in the constructor:
[Binding]
public class MyHooks
{
private ScenarioContext _scenarioContext;
public MyHooks(ScenarioContext scenarioContext)
{
_scenarioContext = scenarioContext;
}
[BeforeScenario]
public void SetupTestUsers()
{
//_scenarioContext...
}
}
Note: for static hook methods you can use parameter injection.
Using Hooks with Parameter Injection¶
You can add parameters to your hook method that will be automatically injected by SpecFlow.
For example you can get the ScenarioContext
injected as parameter in the BeforeScenario hook.
[Binding]
public class MyHooks
{
[BeforeScenario]
public void SetupTestUsers(ScenarioContext scenarioContext)
{
//scenarioContext...
}
}
Parameter injection is especially useful for hooks that must be implemented as static methods.
[Binding]
public class Hooks
{
[BeforeFeature]
public static void SetupStuffForFeatures(FeatureContext featureContext)
{
Console.WriteLine("Starting " + featureContext.FeatureInfo.Title);
}
}
In the BeforeTestRun hook you can resolve test thread specific or global services/dependencies as parameters.
[BeforeTestRun]
public static void BeforeTestRunInjection(ITestRunnerManager testRunnerManager, ITestRunner testRunner)
{
//All parameters are resolved from the test thread container automatically.
//Since the global container is the base container of the test thread container, globally registered services can be also injected.
//ITestRunManager from global container
var location = testRunnerManager.TestAssembly.Location;
//ITestRunner from test thread container
var threadId = testRunner.ThreadId;
}
Depending on the type of the hook the parameters are resolved from a container with the corresponding lifecycle.
Attribute | Container |
---|---|
[BeforeTestRun] [AfterTestRun] |
TestThreadContainer |
[BeforeFeature] [AfterFeature] |
FeatureContainer |
[BeforeScenario] [AfterScenario] [BeforeScenarioBlock] [AfterScenarioBlock] [BeforeStep] [AfterStep] |
ScenarioContainer |
Hook Execution Order¶
By default the hooks of the same type (e.g. two [BeforeScenario]
hook) are executed in an unpredictable order. If you need to ensure a specific execution order, you can specify the Order
property in the hook’s attributes.
[BeforeScenario(Order = 0)]
public void CleanDatabase()
{
// we need to run this first...
}
[BeforeScenario(Order = 100)]
public void LoginUser()
{
// ...so we can log in to a clean database
}
The number indicates the order, not the priority, i.e. the hook with the lowest number is always executed first.
If no order is specified, the default value is 10000. However, we do not recommend on relying on the value to order your tests and recommend specifying the order explicitly for each hook.
Note: If a hook throws an unhandled exception, subsequent hooks of the same type are not executed. If you want to ensure that all hooks of the same types are executed, you need to handle your exceptions manually.
Note: If a BeforeScenario
throws an unhandled exception then all the scenario steps will be marked as skipped and the ScenarioContext.ScenarioExecutionStatus
will be set to TestError
.
Tag Scoping¶
Most hooks support tag scoping. Use tag scoping to restrict hooks to only those features or scenarios that have at least one of the tags in the tag filter (tags are combined with OR). You can specify the tag in the attribute or using scoped bindings.