TestContext in steps binding class

1,945 views
Skip to first unread message

John Smith

unread,
Sep 8, 2011, 1:49:24 AM9/8/11
to SpecFlow
Is it possible to access the MSTest TestContext from within a SpecFlow
(1.7.1) step binding class? In the generated code of a feature file
there is a method FeatureSetup which takes the TestContext as an
argument but apparently doesn't do anything with it.

Gáspár Nagy

unread,
Sep 8, 2011, 2:14:30 AM9/8/11
to SpecFlow
By default not. We have a test-provider independent
ScenarioContext.Current that can be used for similar purposes.

Mario Majcica

unread,
Apr 2, 2015, 5:35:08 AM4/2/15
to spec...@googlegroups.com
Is there a way or chance to have values of TestContext copied to ScenarioContext?

I need to access some values in TestContext inside my binding class.

Ex.  string selectedBrowser = TestContext.Properties["__Tfs_TestConfigurationName__"].ToString();

Gáspár Nagy

unread,
Apr 8, 2015, 11:38:30 AM4/8/15
to spec...@googlegroups.com
Is this some value that TFS sets externally to TestContext and you would need to read it out?

Mario Majcica

unread,
Apr 8, 2015, 12:00:46 PM4/8/15
to spec...@googlegroups.com
This are set by MTM. If you trigger the run of your tests via MTM this will get populated with several items. Check this link for more details http://fluentbytes.com/switching-browser-in-codedui-or-selenium-tests-based-on-mtm-configuration/.

I solved it via a class that has a method with AssemblyInitialize attribute on it. In that way I can access the Context and save in a static variable the value I'm interested in.

WT

unread,
Aug 11, 2015, 1:37:18 AM8/11/15
to SpecFlow
Hey Mario, would you mind posting an example of the class you created with the AssemblyInitialize attribute?

Mario Majcica

unread,
Aug 11, 2015, 1:42:54 AM8/11/15
to spec...@googlegroups.com

I'm on holiday and without the access to my pc. Still ill try to explain it further. Just create a ms unit test class in your project (the one decorated with unit test attribute), then add a method and set on it AssemblyInitialize attribute. Due to the ms test internals it will be always triggered before any test run, and context will be populated.
If I'm not clear I'll post a full example in two weeks time.

Cheers

--
You received this message because you are subscribed to a topic in the Google Groups "SpecFlow" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/specflow/WwOOPig_2_4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to specflow+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Steven Farnell

unread,
Aug 18, 2015, 3:30:39 PM8/18/15
to SpecFlow
I found a pretty good answer to this question by referencing these three sources:


It will walk you through the process of creating a SpecFlow plugin that will add the MS TestContext to your ScenarioContext so it can accessed like so:

TestContext testContext = (TestContext)ScenarioContext.Current["MSTestContext"];

I don't know why the MSTest unit test provider for SpecFlow wouldn't just do this by default, but I guess they have their reasons.

For those of you launching your SpecFlow tests from MTM, I would advise that you remember TestContext will remain the same for multiple tests run at the same time.  For example, I use the "__Tfs_TestConfigurationName__" property to trigger behaviors based on the configuration name, but if I try to select tests with multiple configurations then click run, they will all run with one configuration.  

Just to summarize, here are the step-by-step instructions I followed:
  1. Create a new Class Library project (I chose the name MtmContext.Generator.SpecFlowPlugin)
  2. Use NuGet to add the SpecFlow.CustomPlugin package
  3. Create a new class (I chose MsTestCustomGeneratorProvider.cs)
  4. Paste this into the cs file (replace the names if you don't choose the same as me)
    using MtmContext.Generator.SpecflowPlugin;
    using TechTalk.SpecFlow.Infrastructure;


    [assembly: GeneratorPlugin(typeof(MyGeneratorPlugin))]


    namespace MtmContext.Generator.SpecflowPlugin
    {
       
    using BoDi;
       
    using System.CodeDom;
       
    using TechTalk.SpecFlow.Generator;
       
    using TechTalk.SpecFlow.Generator.Configuration;
       
    using TechTalk.SpecFlow.Generator.Plugins;
       
    using TechTalk.SpecFlow.Generator.UnitTestProvider;
       
    using TechTalk.SpecFlow.Utils;

       
    public class MsTestCustomGeneratorProvider : MsTest2010GeneratorProvider
       
    {
           
    /// <summary>
           
    /// Initializes a new instance of the <see cref="MsTestCustomGeneratorProvider"/> class.
           
    /// </summary>
           
    /// <param name="codeDomHelper">
           
    /// The code dom helper.
           
    /// </param>
           
    public MsTestCustomGeneratorProvider(CodeDomHelper codeDomHelper)
               
    : base(codeDomHelper)
           
    {
           
    }


           
    /// <summary>
           
    /// The finalize test class.
           
    /// </summary>
           
    /// <param name="generationContext">
           
    /// The generation context.
           
    /// </param>
           
    public override void FinalizeTestClass(TestClassGenerationContext generationContext)
           
    {
               
    base.FinalizeTestClass(generationContext);


               
    var msTestContextGeneration =
                   
    new CodeExpressionStatement(
                       
    new CodeMethodInvokeExpression(
                           
    new CodeTypeReferenceExpression("testRunner.ScenarioContext"),
                           
    "Add",
                           
    new CodeExpression[]
                           
    {
                               
    new CodePrimitiveExpression("MSTestContext"),
                               
    new CodeArgumentReferenceExpression("TestContext")
                           
    }));


                generationContext
    .ScenarioInitializeMethod.Statements.Add(msTestContextGeneration);


               
    var msTestAssignment =
                   
    new CodeAssignStatement(
                       
    new CodeVariableReferenceExpression("myTestContext"),
                       
    new CodeVariableReferenceExpression("testContext"));
                generationContext
    .TestClassInitializeMethod.Statements.Add(msTestAssignment);


               
    var mstestContextFiled = new CodeMemberField
               
    {
                   
    Attributes = MemberAttributes.Private | MemberAttributes.Static | MemberAttributes.Final,
                   
    Name = "myTestContext",
                   
    Type = new CodeTypeReference(TESTCONTEXT_TYPE),
               
    };


                generationContext
    .TestClass.Members.Add(mstestContextFiled);


               
    var mstestContextProperty = new CodeMemberProperty();
                mstestContextProperty
    .Name = "TestContext";
                mstestContextProperty
    .Type = new CodeTypeReference(TESTCONTEXT_TYPE);
                mstestContextProperty
    .Attributes =
                         
    (mstestContextProperty.Attributes & ~MemberAttributes.AccessMask) |
                             
    MemberAttributes.Public;
                mstestContextProperty
    .GetStatements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("myTestContext")));
                mstestContextProperty
    .SetStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("myTestContext"), new CodePropertySetValueReferenceExpression()));


                generationContext
    .TestClass.Members.Add(mstestContextProperty);
           
    }


           
       
    }


       
       
    public class MyGeneratorPlugin : IGeneratorPlugin
       
    {
           
    public void RegisterDependencies(ObjectContainer container)
           
    {
           
    }


           
    public void RegisterCustomizations(ObjectContainer container, SpecFlowProjectConfiguration generatorConfiguration)
           
    {
                container
    .RegisterTypeAs<MsTestCustomGeneratorProvider, IUnitTestGeneratorProvider>();
           
    }


           
    public void RegisterConfigurationDefaults(SpecFlowProjectConfiguration specFlowConfiguration)
           
    {
           
    }
       
    }
    }

  5. Build the dll file and copy it into the solution for your specflow project (I chose PROJECT_FOLDER\SpecFlow\Plugins)
  6. Add a specflow plugin to your app.config file
    <add name="MtmContext" type="Generator" path=".\SpecFlow\Plugins"/>
  7. When you save your app.config file, it should rebuild your feature.cs files.
  8.  You should see the following private variable in your feature.cs file:
    private static Microsoft.VisualStudio.TestTools.UnitTesting.TestContext myTestContext;
  9. You should see the following line added to the end of the FeatureSetup(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext testContext) function:
    myTestContext = testContext;
  10. You should see the following line added to the end of the ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) function:
  11. testRunner.ScenarioContext.Add("MSTestContext", TestContext);
  12. you code should successfully be able to access the MTM context via  MSTestContext key in ScenarioContext like this: 
    TestContext testContext = (TestContext)ScenarioContext.Current["MSTestContext"];
I hope someone finds this helpful... 

Gáspár Nagy

unread,
Aug 1, 2016, 2:33:51 PM8/1/16
to SpecFlow
I like this. I think this should be part of the normal MsTest provider...

Roberto Bonini

unread,
Sep 13, 2016, 6:53:19 AM9/13/16
to SpecFlow
That would be most useful Gáspár. Thanks.
Message has been deleted

Jayson Tompkins

unread,
Jan 5, 2017, 6:41:54 PM1/5/17
to SpecFlow
Extremely helpful. 

Here is snippet which also includes the needed Initialize method for the IGeneratorPlugin:

    public class MyGeneratorPlugin : IGeneratorPlugin
    {

        public void Initialize(GeneratorPluginEvents generatorPluginEvents, GeneratorPluginParameters generatorPluginParameters)
        {
            generatorPluginEvents.CustomizeDependencies += this.CustomizeDependencies;
        }

        public void CustomizeDependencies(object sender, CustomizeDependenciesEventArgs eventArgs)
        {
            eventArgs.ObjectContainer.RegisterTypeAs<MsTestCustomGeneratorProvider, IUnitTestGeneratorProvider>();
        }
    }
Reply all
Reply to author
Forward
0 new messages