ScenarioContext
Most of us have at least seen the ScenarioContext from the the code that SpecFlow generates when a missing step definition is found: ScenarioContext.Current.Pending();
But there is some other interesting stuff you can do with and get from that object. I’ve tried to write scenarios that show that off. You can find the code here.
ScenarioContext.Pending
Well – this method is known to most of us, as I said. This is default behavior for a missing step definition, but you can also use it directly if (why?) you want to. Like this:
in the .feature:
Scenario: Pending step
When I set the ScenarioContext.Current to pending
Then this step will not even be executed
and the step definition:
[When("I set the ScenarioContext.Current to pending")]
public void WhenIHaveAPendingStep()
{
ScenarioContext.Current.Pending();
}
[Then("this step will not even be executed")]
public void ThisStepWillNotBeExecuted()
{
throw new Exception("See!? This wasn't even thrown");
}
ScenarioContext.Current
This is a very useful feature of the ScenarioContext.Current that helps you to store values in a Dictionary between the steps. This helps you to organize your step definitions better than if you would use private variables in the step definition class.
There are some type safe extension methods that helps you to get values in and out of the dictionary in a safer way. To get that you need to include the namespace TechTalk.SpecFlow.Assist, since they are extension methods on the ScenarioContext.Current.
ScenarioContext.ScenarioInfo
You can also get hold of some information about the scenario you’re executing right now. For example the title and the tags of it:
In the .feature file:
@showUpInScenarioInfo @andThisToo
Scenario: Showing information of the scenario
When I execute any scenario
Then the ScenarioInfo contains the following information
| Field | Value |
| Tags | showUpInScenarioInfo, andThisToo |
| Title | Showing information of the scenario |
and in the step definition:
private class ScenarioInformation
{
public string Title { get; set; }
public string[] Tags { get; set; }
}
[When(@"I execute any scenario")]
public void ExecuteAnyScenario(){}
[Then(@"the ScenarioInfo contains the following information")]
public void ScenarioInfoContainsInterestingInformation(Table table)
{
// Create our small DTO for the info from the step
var fromStep = table.CreateInstance<ScenarioInformation>();
fromStep.Tags = table.Rows[0]["Value"].Split(',');
// Short-hand to the scenarioInfo
var si = ScenarioContext.Current.ScenarioInfo;
// Assertions
si.Title.Should().Equal(fromStep.Title);
for (var i = 0; i < si.Tags.Length -1; i++)
{
si.Tags[i].Should().Equal(fromStep.Tags[i]);
}
}
More interesting maybe is the ability to check if an error has occurred. That’s done with the ScenarioContext.Current.TestError property, which simply is the exception that has occurred.
You can use that to do some interesting “error handling” as I told you above. Here is an un-interesting version:
in the .feature file:
#This is not so easy to write a scenario for but I've created an AfterScenario-hook
@showingErrorHandling
Scenario: Display error information in AfterScenario
When an error occurs in a step
and the step definition:
[When("an error occurs in a step")]
public void AnErrorOccurs()
{
"not correct".Should().Equal("correct");
}
[AfterScenario("showingErrorHandling")]
public void AfterScenarioHook()
{
if(ScenarioContext.Current.TestError != null)
{
var error = ScenarioContext.Current.TestError;
Console.WriteLine("An error ocurred:" + error.Message);
Console.WriteLine("It was of type:" + error.GetType().Name);
}
}
This is another example, that might be more useful:
[AfterScenario]
public void AfterScenario()
{
if(ScenarioContext.Current.TestError != null)
{
WebBrowser.Driver.CaptureScreenShot(ScenarioContext.Current.ScenarioInfo.Title);
}
}
Here I am using MvcContrib to capture the screen of the failing test, and naming the screen shot after the title of the Scenario. So by combining the use of hooks and the knowledge of ScenarioContext when can do some interesting stuff, don’t you think?
ScenarioContext.Current.CurrentScenarioBlock
You can also get hold of the “type” of step your on (Given, When or Then) which is pretty cool and, for example, can be used to execute additional setup/cleanup code right before or after Given, When or Then block.
in the .feature file:
Scenario: Show the type of step we're currently on
Given I have a Given step
And I have another Given step
When I have a When step
Then I have a Then step
and the step definition:
[Given("I have a (.*) step")]
[Given("I have another (.*) step")]
[When("I have a (.*) step")]
[Then("I have a (.*) step")]
public void ReportStepTypeName(string expectedStepType)
{
var stepType = ScenarioContext.Current.CurrentScenarioBlock.ToString();
stepType.Should().Equal(expectedStepType);
}
ScenarioContext.Current.StepContext
Sometimes you need to access the currently executed step, e.g. to improve tracing. Use the ScenarioContext.Current.StepContext
property for this purpose.
Injecting ScenarioContext
Using the static ScenarioContext.Current
accessor may result in test automation code that is hard to follow, and does not work with parallel execution. In such cases, you can let SpecFlow inject the current scenario context into your binding class using the context injection mechanism by declaring a ScenarioContext
parameter in the constructor, e.g.:
public class MyBindingClass
{
private ScenarioContext scenarioContext;
public MyBindingClass(ScenarioContext scenarioContext)
{
this.scenarioContext = scenarioContext;
}
[When("I say hello to ScenarioContext")]
public void WhenISayHello()
{
scenarioContext["say"] = "hello";
}
}