We're trying to implement global hooks on our specflow tests and are not entirely sure how [BeforeScenario]
and [AfterScenario]
attributed methods work.
The way I've seen it done, those attributes are always defined in a class containing specific steps used in a few scenarios.
Can they go somewhere so they apply to all scenarios? Or does attributing the methods with [BeforeScenario]
and [AfterScenario]
cause them to be run for all scenarios, regardless of where they're actually placed?
Hmm... From what I knew and according to the documentation these hooks are always global, i.e. from http://www.specflow.org/documentation/hooks/
Hooks
The hooks (event bindings) can be used to perform additional automation logic on specific events, like before the execution of a scenario.
The hooks are global but can be restricted to run only for features or scenarios with a specific tag (see below). The execution order of hooks for the same event is undefined.
In fact by producing a small demo project with the following
[Binding]
public class Unrelated
{
[BeforeScenario]
public void WillBeCalledIfGlobal()
{
Console.WriteLine("I'm global");
}
}
[Binding]
public class JustTheTest
{
[Given("nothing")]
public void GivenNothing()
{
// Don't do anything
}
}
Then the test specification of
As a developer
In order to understand how BeforeSpecifcation works
I want to know what the following does
Scenario: See if BeforeSpecifcation hook gets called
Given nothing
The get the output
I'm global
Given nothing
-> done: JustTheTest.GivenNothing() (0.0s)
So it really does look as if the documentation is correct, and you should use tagging to control if the BeforeScenario
\ AfterScenario
are run before or after your scenario.
There is also a good example of how tagging works here -> Feature-scoped step definitions with SpecFlow?
Yes, you can create global BeforeScenario and AfterScenario methods, but in practice I find that this is not desirable, as usually the same before and after steps do not apply to all steps in a test project.
Instead I create a base class for my step definitions, which would have the BeforeScenario and AfterScenarios methods I'd like applied to all of my scenarios e.g.
public class BaseStepDefinitions
{
[BeforeScenario]
public void BeforeScenario()
{
// BeforeScenario code
}
[AfterScenario]
public void AfterScenario()
{
// AfterScenario code
}
}
Note that I have not used the Binding attribute on this class. If you do include it then the BeforeScenario and AfterScenario steps would be global.
I then derive my step definion classes from this base step definition class, so that they will have the Before and After scenario methods e.g.
[Binding]
public class SpecFlowFeature1Steps : BaseStepDefinitions
{
[Given(@"I have entered (.*) into the calculator")]
public void GivenIHaveEnteredIntoTheCalculator(int inputValue)
{
ScenarioContext.Current.Pending();
}
[When(@"I press add")]
public void WhenIPressAdd()
{
ScenarioContext.Current.Pending();
}
[Then(@"the result should be (.*) on the screen")]
public void ThenTheResultShouldBeOnTheScreen(int expectedResult)
{
ScenarioContext.Current.Pending();
}
}
Whilst this approach is not global, by making all StepDefinitions derive from a BaseStepDefinition class we achieve the same outcome.
It also gives more control i.e. if you don't want the BeforeScenario or AfterScenario binding then don't derive from the base steps.
Sorry this doesn't work. As soon as you start using multiple Binding classes you end up with multiple calls. For example if I extend the example above to split the bindings into three classes,
[Binding]
public class SpecFlowFeature1Steps : BaseStepDefinitions
{
[Given(@"I have entered (.*) into the calculator")]
public void GivenIHaveEnteredIntoTheCalculator(int inputValue)
{
//ScenarioContext.Current.Pending();
}
}
[Binding]
public class SpecFlowFeature2Steps : BaseStepDefinitions
{
[When(@"I press add")]
public void WhenIPressAdd()
{
//ScenarioContext.Current.Pending();
}
}
[Binding]
public class SpecFlowFeature3Steps : BaseStepDefinitions
{
[Then(@"the result should be (.*) on the screen")]
public void ThenTheResultShouldBeOnTheScreen(int expectedResult)
{
//ScenarioContext.Current.Pending();
}
}
public class BaseStepDefinitions
{
[BeforeScenario]
public void BeforeScenario()
{
// BeforeScenario code
Console.WriteLine("Before. [Called from "+ this.GetType().Name+"]");
}
[AfterScenario]
public void AfterScenario()
{
// AfterScenario code
Console.WriteLine("After. [Called from " + this.GetType().Name + "]");
}
}
Then when I run it, the output is
Before. [Called from SpecFlowFeature1Steps]
Before. [Called from SpecFlowFeature2Steps]
Before. [Called from SpecFlowFeature3Steps]
Given I have entered 50 into the calculator
-> done: SpecFlowFeature1Steps.GivenIHaveEnteredIntoTheCalculator(50) (0.0s)
And I have entered 70 into the calculator
-> done: SpecFlowFeature1Steps.GivenIHaveEnteredIntoTheCalculator(70) (0.0s)
When I press add
-> done: SpecFlowFeature2Steps.WhenIPressAdd() (0.0s)
Then the result should be 120 on the screen
-> done: SpecFlowFeature3Steps.ThenTheResultShouldBeOnTheScreen(120) (0.0s)
After. [Called from SpecFlowFeature1Steps]
After. [Called from SpecFlowFeature2Steps]
After. [Called from SpecFlowFeature3Steps]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With