Tuesday 2 August 2011

SpecFlow custom UnitTestProvider (Code generator) for Silverlight to access the Silverlight Test instance

When upgrading to Specflow 1.7 from the Async 2 branch, I have noticed that the new generator did not give you access to the Silverlight Test Instance. (This has been solved now https://github.com/techtalk/SpecFlow/pull/126, but it will be interesting to know how to solve this issue by creating your own unit test provider).

First you need to create a class which implements IUnitTestGeneratorProvider, in my case I have inherited directly from the MsTestSilverlightGeneratorProvider as follows. (Please note the code it is more or less the same as the original implementation).


public class SiverlightWithTestInstanceCustomGeneratorUnitTestProvider : MsTestSilverlightGeneratorProvider

{
public override void FinalizeTestClass(TechTalk.SpecFlow.Generator.TestClassGenerationContext generationContext)
{

base.FinalizeTestClass(generationContext);

var methods = from codeTypeMember in generationContext.Namespace.Types[0].Members.Cast()
where codeTypeMember.Name == "ScenarioSetup"
select (CodeMemberMethod)codeTypeMember;

var scenarioSetupMethod = methods.First();

var scenarioContext = new CodeTypeReferenceExpression("ScenarioContext");
var currentContext = new CodePropertyReferenceExpression(scenarioContext, "Current");

var scenarioContextExtensions = new CodeTypeReferenceExpression("ScenarioContextExtensions");

var setTestInstance = new CodeMethodInvokeExpression(scenarioContextExtensions, "SetTestInstance",
currentContext, new CodeThisReferenceExpression());

// Hmm. We have a little too much knowledge of where this should go, but it's after the call
// to testRunner.OnScenarioStart, and before FeatureBackground
scenarioSetupMethod.Statements.Insert(2, new CodeExpressionStatement(setTestInstance));
}
}




The class in this project, inserts the following line in the generated test:


   1:  ScenarioContextExtensions.SetTestInstance(ScenarioContext.Current, this);



public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)

{
TechTalk.SpecFlow.Async.AsyncTestRunner.RegisterAsyncTestExecutor(testRunner, new TechTalk.SpecFlow.Async.SilverlightAsyncTestExecutor(this));
testRunner.OnScenarioStart(scenarioInfo);
ScenarioContextExtensions.SetTestInstance(ScenarioContext.Current, this);
this.FeatureBackground();
}




This is necessary if you want to get access of the SiverlightTest like:

var silverlightTest = ScenarioContext.Current.GetTestInstance<SilverlightTest>();

to do things like:

 silverlightTest.TestPanel.Children.Add(myView);


I have not figured out if it is possible to configure specflow to read this from a different assembly, so for the time being you can
1. Get the version of Specflow you are currently working on

2. Add the class to the project TechTalk.SpecFlow.Generator. In the folder UnitTestProvider.


3. Compile and put the new dll where SpecFlow is configured, in case of doubt see the example of configuration bellow:


<generator path="..\..\packages\SpecFlow.1.7.0\tools" generateasynctests="true"></generator>


To run your custom generator add this line to your app.config, please note that I have given a random name to my unitTestProvider, (not matching the generic ones)


<unittestprovider name="customSL" generatorprovider="TechTalk.SpecFlow.Generator.UnitTestProvider.SiverlightWithTestInstanceCustomGeneratorUnitTestProvider" />