Is it possible to programmatically add lines to a scenario?
specflow
Solution
OK, I figured this out. It took a few steps:
After looking through the source code on GitHub, I found `UnitTestFeatureGenerator` which appears to be the class responsible for turning the specflow files into unit tests.
I then created a new class that inherited from `UnitTestFeatureGenerator` and hides the `GenerateUnitTestFixture` method from the base class.
In the body of my `GenerateUnitTestFixture` class, I then add the extra steps required into the scenario before handing off of `base.GenerateUnitTestFixture` to generate the unit tests. Here's the gist of it:
public class MultiThemeUnitTestFeatureGenerator : UnitTestFeatureGenerator, IFeatureGenerator
{
public MultiThemeUnitTestFeatureGenerator(IUnitTestGeneratorProvider testGeneratorProvider, CodeDomHelper codeDomHelper, GeneratorConfiguration generatorConfiguration, IDecoratorRegistry decoratorRegistry)
: base(testGeneratorProvider, codeDomHelper, generatorConfiguration, decoratorRegistry)
{}
public new CodeNamespace GenerateUnitTestFixture(Feature feature, string testClassName, string targetNamespace)
{
foreach (var scenario in feature.Scenarios)
{
scenario.Steps.Insert(0, new Given {Text = "Given I have <Theme> set as my current theme"});
//add any other steps you need....
}
return base.GenerateUnitTestFixture(feature, testClassName, targetNamespace);
}
}
Once I had all of this set up, I needed a way to tell specflow to use my new class instead of the currently registered `UnitTestFeatureGenerator`. This was the complicated bit to get working as the documentation pretty much just said "Coming soon". Thankfully, I found an excellent blog post which outlines all the pitfalls.
My `IGeneratorPlugin` implementation looks like this:
public class MultiThemeGeneratorPlugin : IGeneratorPlugin
{
public void RegisterDependencies(ObjectContainer container)
{}
public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
{
container.RegisterTypeAs<MultiThemeFeatureGeneratorProvider, IFeatureGeneratorProvider>("default");
}
public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
{}
}
Note That I register `IFeatureGeneratorProvider` as opposed to `IFeatureGenerator`. I had to create an implementation of `IFeatureGeneratorProvider` that returns an instance of the implementation of `IFeatureGenerator` that I was interested in:
public class MultiThemeFeatureGeneratorProvider : IFeatureGeneratorProvider
{
private readonly ObjectContainer _container;
public MultiThemeFeatureGeneratorProvider(ObjectContainer container)
{
_container = container;
}
public int Priority
{
get { return int.MaxValue; }
}
public bool CanGenerate(Feature feature)
{
return true;
}
public IFeatureGenerator CreateGenerator(Feature feature)
{
return _container.Resolve<MultiThemeUnitTestFeatureGenerator>();
}
}
Problem
I would like to add the same line to the start of each one of my SpecFlow tests. This line specifies a list of several scenarios which will change over time, and therefore it is not feasible to maintain this list for every test. For example: ``` Given I have set my site theme to <MyTheme> |Theme Names| |Theme 1 | |Theme 2 | |Theme 3 | |Theme 4 | |Theme 5 | ``` I'd like to have this test repeat for each of the themes. The list of themes is not set in stone, and should be maintained in a single place. So far, I've successfully managed to create a `Generator Plugin`, and I was planning on using this plugin to alter the SpecFlow feature immediately before the Test Class is generated. However, I can't see a way to edit the scenario from within this context. Is it possible to get and set the scenario text from within an implementation of `IUnitTestGeneratorProvider`? I'm not set on using this method, so if anyone can suggest a better way to do this then I'd accept that too. Apologies if I've gotten some terminology wrong- I've only just started using SpecFlow. Edit: I'm adding this section to provide clarification as to what I'm actually after. Suppose I had a test suite containing 800 tests. We have a business requirement to run each of those 800 tests on each of our available themes. The list of available themes can change at any time, and it would be unfeasible to maintain this list in more than a single location. So, for example, if I had the following two tests: Example A: ``` Given I set context to < site > Given I go to base url When I type <username> in username field When I type <password> in password field When I click login button Examples: | site | username | password | | MySuperSite | chris | mypassword | | MySuperSite2 | chris2 | mypassword | Given I am logged in Given I go to base url When I click logout button Then I am logged out ``` I could simply manually change these tests to something along the lines of: Example B: ``` Given I am using the < theme > theme Given I set context to < site > Given I go to base url When I type <username> in username field When I type <password> in password field When I click login button Examples: | site | username | password | theme | | MySuperSite | chris | mypassword | theme1 | | MySuperSite2 | chris2 | mypassword | theme1 | | MySuperSite | chris | mypassword | theme2 | | MySuperSite2 | chris2 | mypassword | theme2 | | MySuperSite | chris | mypassword | theme3 | | MySuperSite2 | chris2 | mypassword | theme3 | Given I am using the < theme > theme Given I am logged in Given I go to base url When I click logout button Then I am logged out Examples: | theme | | theme1 | | theme2 | | theme3 | ``` There are a few issues with this: - The tests become bloated with data that is repeated - If we no longer choose to support `theme2` then someone will have to go through each test and remove it from the examples table (not too bad in the example above, but imaging we had >800 tests) - Tests that already have example tables will have their table size multiplied by the number of themes we are supporting (>40) - There is a large risk of manual error caused by someone inadvertently forgetting to add a specific theme to a test Objective: I'd like to be able to have our testers write tests in the style of `Example A`, but have the tests themselves compile down to how they would if they were written in the style of `Example B`. I've created a generator plugin for specflow, with a view of intercepting the test creation and then programmatically adding the line `Given I am using the < theme > theme`, and then update or add any example data as required. However, I can't seem to be able to do this from here. Can anyone tell me if this is possible, and if so, how should I go about it?