Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpecFlow: ClassInitialize and TestContext

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
like image 992
robert.oh. Avatar asked Aug 31 '12 15:08

robert.oh.


3 Answers

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
    } 
}
like image 171
Andreas Willich Avatar answered Nov 12 '22 07:11

Andreas Willich


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.

like image 42
codevision Avatar answered Nov 12 '22 08:11

codevision


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/

like image 22
Marksl Avatar answered Nov 12 '22 06:11

Marksl