Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to programmatically add lines to a scenario?

Tags:

specflow

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?

like image 661
Chris Payne Avatar asked Jul 07 '14 12:07

Chris Payne


2 Answers

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>();
    }
}
like image 101
Chris Payne Avatar answered Oct 21 '22 08:10

Chris Payne


I can't think of a simple way to do this, but my approach would probably be to exclude the theme from the tests but have the build server run the tests in an environment where the theme is already set up (by an environment variable or via some config file or similar) and then run the entire suite n times, once for each theme.

So I would have the scenarios look like this

Given I am using the currently configured theme
Given I am logged in
Given I go to base url
When I click logout button
Then I am logged out

where the first step reads the theme from the config file or environment variable and sets up the tests to use that theme.

Once the build server has run these tests, it would change the config/environment variable to the next theme and the run the suite again.

This gives you the ability to specify the themes in a single place (on the build server, or in a file that the BS uses to discover the themes) and have the tests run using each of those themes. It also might give you some performance advantage as you could parallelize the tests on the build server as each one would be a different test step and each test step could be run independently and on different machines.

For the devs you would only be able to test one theme at once and would have to manually alter the config/env variable to change to a different theme, which might not be ideal.

Someone else might have a better idea of how to do this in the way you are envisioning, fingers crossed

like image 37
Sam Holder Avatar answered Oct 21 '22 08:10

Sam Holder