Keith VanderVeen
Keith VanderVeen

Reputation: 111

Inheritance in Specflow test steps cause Ambiguous Steps

I've read that using inheritance is not possible when using Specflow, which makes sense most of the time. However, I've run across a situation that seems to require the proper the use of inheritance. Here are my classes:

Base Class:

public class StepBaseClass
{
    protected readonly ScenarioContext scenarioContext;

    public StepBaseClass(ScenarioContext scenarioContext)
    {
        this.scenarioContext = scenarioContext;
    }
}

First Inherited Class:

    [Binding]
    public class StudioEnterpriseImportConnectorSteps:StepBaseClass
    {
        public StudioEnterpriseImportConnectorSteps(ScenarioContext scenarioContext) :base(scenarioContext)
        {

        }
        [Given(@"I have a data record that I want to send to the import service")]
        public void GivenIHaveADataRecordThatIWantToSendToTheImportService()
        {
            scenarioContext.Pending();
        }

        [When(@"I send the information to an invalid URL")]
        public void WhenISendTheInformationToAnInvalidURL()
        {
            scenarioContext.Pending();
        }

        [Then(@"an error should be generated")]
        public void ThenAnErrorShouldBeGenerated()
        {
            scenarioContext.Pending();
        }
    }

2nd inherited class:

    [Binding]
    public class SitemapSteps:StepBaseClass
    {
        public SitemapSteps(ScenarioContext scenarioContext):base(scenarioContext)
        {
        }
        [When(@"I visit the URL (.*)")]
        public void WhenIVisitTheSitemapURL(string URL)
        {
            scenarioContext.Add("result", TestUtilities.GetResponseCode(URL));
            scenarioContext.Add("response", TestUtilities.GetResponseBody(URL));
        }

        [Then(@"the response code should be (.*)")]
        public void ThenTheResponseCodeShouldBe(string responseCode)
        {
            HttpStatusCode result = scenarioContext.Get<HttpStatusCode>("result");
            Assert.Equal(responseCode, result.ToString());
        }
    }

As you can see, the only thing that I'm inheriting the the scenarioContext, which is something that I need to do in order to write multi-threaded tests. So instead of repeating this piece of code for each of my classes, I would like to be able to inherit from a base class. What is the proper method of initializing that variable so that I can use it in each of my derived classes?

Upvotes: 1

Views: 859

Answers (2)

Andreas Willich
Andreas Willich

Reputation: 5825

The proper way depends as always on your individual situaion. I recommend always to not use base classes and use context injection everywhere. The small number of code that is repeated in the constructor is a small price for a good separation and splitting of your bindings and their implementation.

To get more info about this topic, Gaspar Nagy wrote a nice blog article about the pros and cons of step base classes in SpecFlow: http://gasparnagy.com/2017/02/specflow-tips-baseclass-or-context-injection/

Upvotes: 1

ViqMontana
ViqMontana

Reputation: 5688

After initializing my Dependency Injection in the Specflow Test hooks, I would have a class called ApplicationContext with a static resolve method which would return me my ScenarioContext instance like so:

public class ApplicationContext
{
    public static T Resolve<T>() where T: class 
    {
        return container.GetInstance<T>();
    }
}

Then in my steps class, I would resolve the ScenarioContext like this:

scenarioContext = (ScenarioContext)ApplicationContext.Resolve<IScenarioContext>();

Upvotes: 0

Related Questions