first of all I'm new to SpecFlow.
I have a feature file which I have / want to automate using MSTest to run as a functional test involving a fully set up server, data access ...
For this purpose I have to configure the server with the data in the SpecFlow's 'Given' blocks and start it afterwards. I also have to copy some files to the test's output directory.
In the non-SpecFlow functional tests I was using the ClassInitialize attribute to get the TestDeploymentDir from the TestContext; something like this:
[ClassInitialize]
public static void ClassSetup(TestContext context)
{
TargetDataDeploymentRoot = context.TestDeploymentDir;
}
Now with SpecFlow I can't use this attribute anymore as it is used by SpecFlow itself.
Some new attributes do exist, like BeforeFeature which acts similarly BUT it doesn't pass on the TestContext as a parameter.
I just need to get access to the TestContext's TestDeploymentDir in order to copy some files there before really lauching my functional test server - easily doable without SpecFlow but almost impossible with SpecFlow.
How to deal with this issue?
Is it possible at all?
Thanks a lot for advice!
robert
Environment:
- Visual Studio 2012
- SpecFlow 1.9.0.77
In order to have access to values in the TestContext you have to create partial class for each scenario file you have in which you add the .
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TechTalk.SpecFlow;
/// <summary>
/// Partial class for TestContext support.
/// </summary>
public partial class DistributionFeature
{
/// <summary>
/// Test execution context.
/// </summary>
private TestContext testContext;
/// <summary>
/// Gets or sets test execution context.
/// </summary>
public TestContext TestContext
{
get
{
return this.testContext;
}
set
{
this.testContext = value;
//see https://github.com/techtalk/SpecFlow/issues/96
this.TestInitialize();
FeatureContext.Current["TestContext"] = value;
}
}
}
Then you could access the deployment directory from your steps using
var testContext = (TestContext)FeatureContext.Current["TestContext"];
var deploymentDir = testContext.TestDeploymentDir;
If you have too many scenarios, then you probably has to automate creation of such files with T4.
You can create a Plugin and customize the IUnitTestGeneratorProvider implementation. The following should add the line to MSTest's class initialize.
// It's very important this is named Generator.SpecflowPlugin.
namespace MyGenerator.Generator.SpecflowPlugin
{
public class MyGeneratorProvider : MsTest2010GeneratorProvider
{
public MyGeneratorProvider(CodeDomHelper codeDomHelper)
: base(codeDomHelper)
{
}
public override void SetTestClassInitializeMethod(TestClassGenerationContext generationContext)
{
base.SetTestClassInitializeMethod(generationContext);
generationContext.TestClassInitializeMethod.Statements.Add(new CodeSnippetStatement(
@"TargetDataDeploymentRoot = context.TestDeploymentDir;"));
}
}
[assembly: GeneratorPlugin(typeof(MyGeneratorPlugin))]
public class MyGeneratorPlugin : IGeneratorPlugin
{
public void RegisterDependencies(ObjectContainer container)
{
}
public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
{
container.RegisterTypeAs<MyGeneratorProvider, IUnitTestGeneratorProvider>();
}
public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
{
}
}
}
And reference it in the App.config file:
<specFlow>
<plugins>
<add name="MyGenerator" type="Generator"/>
</plugins>
</specFlow>
Next time you re-save the .feature files the generated code in ClassInitialize should set the TargetDataDeploymentDirectory.
I had to do something similar. Here's my working code https://github.com/marksl/Specflow-MsTest and blog post http://codealoc.wordpress.com/2013/09/30/bdding-with-specflow/
Since SpecFlow 2.2.1 the TestContext is available via Context Injection. (https://github.com/techtalk/SpecFlow/pull/882)
You can get it from the container directly:
ScenarioContext.Current.ScenarioContainer.Resolve<Microsoft.VisualStudio.TestTools.UnitTesting.TestContext>()
or via context injection:
public class MyStepDefs
{
private readonly TestContext _testContext;
public MyStepDefs(TestContext testContext) // use it as ctor parameter
{
_testContext = testContext;
}
[BeforeScenario()]
public void BeforeScenario()
{
//now you can access the TestContext
}
}
There is a FeatureContext as well as the more commonly used
ScenarioContext. The difference of course is that the FeatureContext
exists during the execution of the complete feature while the
ScenarioContext only exists during a scenario.
For example:
Add to context:
ScenarioContext.Current.Add("ObjectName", myObject);
Get:
var myObject = ScenarioContext.Current.Get<object>("ObjectName");
You can read more about it here.