Skip to main content

How do I set the initial values for a specflow context?

Comments

1 comment

  • Sam Shackleton

    Hello Tim,

    Does the following documentation answer your first question?

    Context Injection

    I've linked to the advanced section however you may find it useful to read from the top.

    Just use a BeforeTestRun hook rather than BeforeScenario, so:

    [Binding]
    public class RegistrationHooks
    {
      [BeforeTestRun]
      public static void RegisterDependenciesSharedByAllScenarios(IObjectContainer container)
      {
        RunContext runContext = GetRunContextFromWherever();
        container.RegisterInstanceAs(runContext);
      }
    }

    A few things to keep in mind:

    - The above example creates a single instance of RunContext to be shared by all scenarios. Your example above includes properties with setters. If one scenario was to modify any of the properties in RunContext then it would be modified for all other scenarios as well. You probably don't want that to happen.

    - This is getting a little bit advanced. If you aren't planning to run tests in parallel you can ignore this. If it doesn't make sense right now don't worry, just use the code below instead of the code above :) SpecFlow uses different instances of the object containers with different scopes: Global, Test Thread, Feature, Scenario (more info here). The object container that will be passed in to a BeforeTestRun hook is the TestThreadContainer. When running features in parallel there will be multiple threads running tests. Each thread gets its own TestThreadContainer however the BeforeTestRunHook only runs for the first thread. Therefore, the RunContext instance will only be registered in the first thread. Any tests running on other threads will not have access to the RunContext.

    You can get around this by still creating only one instance of the RunContext but registering it in a BeforeFeature hook instead:

    [Binding] 
    public class RegistrationHooks
    {
    private static RunContext _runContext;

    [BeforeTestRun]
    public static void CreateInstanceOfRunContext() {
    // create a single instance but do not register it yet
    _runContext = GetRunContextFromWherever();
    }

    [BeforeFeature]
    public static void RegisterDependenciesInFeatureContainer(IObjectContainer container) {
    // register the same instance in to each feature container
    container.RegisterInstanceAs(_runContext);
    }
    }

    In terms of your last question about passing values in a CICD pipeline it depends on which unit test framework you are using. SpecFlow supports generating tests using MSTest, NUnit, and xUnit. SpecFlow used to support its own test runner, the SpecFlow+ Runner, however it is now discontinued.

    Tests using MSTest, NUnit, and xUnit can all be run using the vstest platform (this is what is being used when running tests via visual studio, or dotnet test, or vstest.console.exe). The vstest platform has first class support for settings via 'RunSettings'. Key-value pairs can be passed in via the TestRunParameters section. Unfortunately, the xUnit adapter for vstest does not support runsettings.

    A nice feature of runsettings is that, as long as you are using a relatively recent version of vstest, you can override runsettings via the command line as described here. You can also create multiple runsettings files and switch between them in Visual Studio.

    Personally I usually use NUnit and create a strong typed static class with read only properties for each  TestRunParameter. I can then access any of these properties from anywhere in the tests without worrying about registering anything in the object container.

     

    0

Please sign in to leave a comment.

Powered by Zendesk