Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can SpecFlow features be shared between Steps?

We have code which we'd like to test from three different angles:

  • Internally (direct call)
  • Web service
  • Web application

As we don't want to write feature files three times, it seems that we should share features between Steps, but I can't figure out how this might be done, aside from perhaps sharing files between VS projects, which has always seemed a bit, well, flaky.

Is sharing feature files between projects the best way to achieve this, or is there a more sensible method?

like image 875
Rik Hemsley Avatar asked Feb 21 '23 09:02

Rik Hemsley


1 Answers

I know this was asked quite a while ago, but what I think you want is possible, and there are several ways to achieve it as I understand. In effect what you really want is to define the feature once but switch out the steps that are called for the feature based on whether you are calling the Internal service, the public webservice or the WebApp. There is a discussion of the various approaches to solve this on the mailing list for specflow but the gist of it is as follows (example show an API vs UI approach but the same applies to your Internal vs web service vs webapp I believe):

Option 1 #

(thanks to Oliver Friedrich from the mailing list)

you need to create one assembly for each of the options you want to vary for the steps, so one for internal, one for web service and one for webapp. You tell specflow about all of the different assemblies in the config, like so:

<specFlow>
  <stepAssemblies>
    <stepAssembly assembly="Tests.API" />
    <stepAssembly assembly="Tests.UI" />
  </stepAssemblies>
</specFlow>

then in your common test steps you have some [BeforeTestRun] step which chooses which assembly to load the steps from:

[Binding]
public class TestRunSetup {

    // this method needs to be static
    [BeforeTestRun]
    public static void BeforeTestRun() 
    {
        if (RunApiTests()) // <-- implement this method to choose whether to run the tests via your chosen method 
        {
            Assembly.Load("Tests.API");
        }
        else 
        {
            Assembly.Load("Tests.UI");
        }
    }
}

Option 2 ##

(thanks to Gáspár Nagy from the mailing list)

Try generating the tests during the build. I'm not sure exactly how this will work but it is an area we can investigate.

Option 3 ##

(thanks to Dan Mork from the mailing list)

If you use SpecFlow's dependency injection capabilities then you can create an interface for executing the calls you want to do and use this in a common set of steps. Then you can have 3 implementations of that interface, one which calls your internal service, one which calls the web service and one which manipulates the web app. All that then remains is to inject the correct implementation of that interface into the specflow step files, which can be done like this:

// the abstract concept of the system that could be implemented with 
Selenium, HttpClient, etc. 
public interface IDocument 
{ 
    string Title { get;} 
    void Load(); 
} 

// the steps that are executed when the scenarios from your feature 
file are executed 
[Binding] 
public class Steps 
{ 
    private readonly IDocument _document; 

    public Steps(IDocument document) 
    { 
        _document = document; 
    } 

    [Given("something")] 
    public void GivenSomething() 
    { 
        // set up given state 
    } 

    [When("I view the document")] 
    public void WhenIViewTheDocument() 
    { 
        _document.Load(); 
    } 

    [Then(@"the title should be ""(.*)""")] 
    public void Then(string title) 
    { 
        Assert.ArEqual(_document.Title, title); 
    } 
} 

// this is where the magic happens - get the dependency injection 
// container and register an IDocument implementation
[Binding] 
public class Dependencies 
{ 
    private readonly IObjectContainer _objectContainer; 

    public Dependencies(IObjectContainer objectContainer) 
    { 
        _objectContainer = objectContainer; 
    } 

    [BeforeScenario] 
    public void RegisterDocumentInterfaces() 
    { 
        // register the correct IDocument implementation - UI or API 
    } 
} 

That still leaves you with the problem of how to know which implementation to register. That will depend on the specifics of your solution, your build environment, your test execution environment, etc. Some options for that...

  • tag parameter to the BeforeScenarioAttribute
  • create two different build configurations for your project that each define different constants and use precompiler directives to build the right registrations into the code
  • add conditional logic to check an environment variable
  • add conditional logic to check a configuration setting
  • I haven't checked, but perhaps ScopeAttribute is extensible for you to create a subclass and provide your own logic for whether or not a BeforeScenarioAttribute method is executed.

Hopefully this will give you some options to do what you want.

IMHO the first option is probably the best, though option 3 is pretty sweet as well.

like image 172
Sam Holder Avatar answered Mar 08 '23 03:03

Sam Holder