Sharing SpecFlow features between test projects
Depending on how the team works it might be useful to share same SpecFlow feature files between different test projects. For example same feature file can be used for Unit and Acceptance tests.
I have setup small project to demonstrate one of the ways the feature files can be shared between different projects in the same solution. The approach used in this example is to use one project as a source and copy new and changed feature files to another project on build. First lets take a look at the feature file
Feature: HelloWorld
In to see that a program works
As programmer
I want to see hello world
Scenario: Print Hello World
Given the SharedFeature program
When SharedFeature program is started
Then the 'Hello World' should be printed on the screen
This scenario can be tested as a unit test
[Binding]
public class HelloWorldSteps
{
private HelloWorldService _helloWorldService;
private Mock<IOutputWriter> _outputWriterMoq;
[Given(@"the SharedFeature program")]
public void GivenTheSharedFeatureProgram()
{
_outputWriterMoq = new Mock<IOutputWriter>();
_helloWorldService = new HelloWorldService(_outputWriterMoq.Object);
}
[When(@"SharedFeature program is started")]
public void WhenSharedFeatureProgramIsStarted()
{
_helloWorldService.SayHello();
}
[Then(@"the '(.*)' should be printed on the screen")]
public void ThenTheShouldBePrintedOnTheScreen(string expectedString)
{
_outputWriterMoq.Verify(writer => writer.WriteLine(expectedString));
}
}
The same scenario can be tested as a black box
and used for acceptance testing
[Binding]
public class HelloWorldSteps
{
private readonly Process _process;
private readonly TaskCompletionSource<string> _processOutputTaskCompletionSource;
public HelloWorldSteps(Process process)
{
_process = process;
_processOutputTaskCompletionSource = new TaskCompletionSource<string>();
}
[Given(@"the SharedFeature program")]
public void GivenTheSharedFeatureProgram()
{
_process.StartInfo = new ProcessStartInfo
{
FileName = "SharedFeature.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
_process.OutputDataReceived += OutputHandler;
}
private void OutputHandler(object sender, DataReceivedEventArgs e)
{
if (_processOutputTaskCompletionSource.Task.IsCompleted) return;
_processOutputTaskCompletionSource.SetResult(e.Data);
}
[When(@"SharedFeature program is started")]
public void WhenSharedFeatureProgramIsStarted()
{
_process.Start();
_process.BeginOutputReadLine();
}
[Then(@"the '(.*)' should be printed on the screen")]
public void ThenTheShouldBePrintedOnTheScreen(string expectedString)
{
var processOutput = _processOutputTaskCompletionSource.Task.Result;
Assert.Equal(expectedString, processOutput);
}
}
To accomplish feature sharing target project has to have following in csproj
file.
- Import of SpecFlow targets
<Import Project="..\packages\SpecFlow.2.2.0\tools\TechTalk.SpecFlow.targets" Condition="Exists('..\packages\SpecFlow.2.2.0\tools\TechTalk.SpecFlow.targets')" />
- Targets that copy
feature
files and regenerate code-behind
<Target Name="BeforeBuild">
<CallTarget Condition="'$(NCrunch)' != '1'" Targets="CopyFeatureFilesIfNewer" />
<CallTarget Condition="'$(NCrunch)' != '1'" Targets="RegenFeatureCodeBehind" />
</Target>
<Target Name="CopyFeatureFilesIfNewer">
<Exec Command="xcopy $(SolutionDir)Tests\Features\*.feature $(SolutionDir)AcceptanceTests\Features\ /d /y /i" />
</Target>
<Target Name="RegenFeatureCodeBehind">
<GenerateAll ShowTrace="$(ShowTrace)" BuildServerMode="$(BuildServerMode)" OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" ProjectPath="$(MSBuildProjectFullPath)" ForceGeneration="$(ForceGeneration)" VerboseOutput="$(VerboseOutput)">
<Output TaskParameter="GeneratedFiles" ItemName="SpecFlowGeneratedFiles" />
</GenerateAll>
</Target>
Note: This solution is only applicable if the projects are in the same repository and feature files will not be copied if tests are run by NCrunch