Can I use SpecFlow functions between steps?

We have a code that we would like to test from three different angles:

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

Since we donโ€™t want to write function files three times, it seems that we should share functions between the Steps, but I canโ€™t figure out how to do this, except perhaps sharing files between VS projects, which always seemed a little good, flaky .

Is sharing function files between projects the best way to achieve this, or is there a smarter method?

+4
source share
6 answers

I know that this was asked a long time ago, but what I think you want, maybe there are several ways to achieve this, as I understand it. In fact, you really want to define a function once, but disable the steps that call for this function, based on whether you are calling an internal service, a public web service, or WebApp. There is a discussion of various approaches to solving this issue in the mailing list , but the essence of this is as follows (the example shows the API vs UI, but the same applies to your internal vs web service vs webapp, which I consider):

Option 1 #

(thanks to Oliver Friedrich from the mailing list)

you need to create one assembly for each of the parameters that you want to change for the steps, so one for internal, one for web service and one for webapp. You say that to tell about all the different assemblies in the config, for example:

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

then in your general test steps you have some kind of step [BeforeTestRun] , which chooses which assembly to load the steps:

 [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 tests at build time . I donโ€™t know exactly how this will work, but this is an area that we can explore.

Option 3 ##

(thanks to Dan Mork from the mailing list)

If you are using SpecFlow's dependency injection capabilities, you can create an interface to make the calls you want to use and use this in a common set of steps. Then you can have 3 implementations of this interface, one of which calls your back-end service that calls the web service, and one that manages the web application. It remains only to enter the correct implementation of this interface into the files of the steps of the stack, which can be done as follows:

 // 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 } } 

This still leaves you a problem with registering. This will depend on the features of your decision, your build environment, test runtime, etc. Some options for this ...

  • for BeforeScenarioAttribute
  • create two different build configurations for your project. each defines different constants and uses precompiler directives to build the correct registrations in the code
  • add conditional logic to check environment variable
  • add conditional logic to verify configuration settings
  • I did not check, but perhaps ScopeAttribute expanding for you should create a subclass and provide your own logic for the BeforeScenarioAttribute method to be BeforeScenarioAttribute .

Hope this gives you some options to do what you want.

IMHO the first option is probably the best, although option 3 is also pretty sweet.

+5
source

We had a similar case when the same functions should work for different input methods. Our solution was similar to option # 3 from Sam Holder's answer, but we wanted to use configuration files instead of code to decide which implementation to use in each case.

This can be achieved through its own DI mechanism. In addition to being able to register implementations at runtime, it also reads the configuration file on the first call and loads the classes described.

Now for each project that shares functions with another, we have a .config file, where we indicate the correct implementation for each common interface (mainly input data).

The limitation with this approach is that you can customize classes without constructors without parameters, but for us this is not a problem.

If you are interested in a solution, I will upload it to github.

+2
source

What worked for us was something similar to Sam Holder's first version.

  • Match both dependencies in the VS project so that both dll libraries are copied to your output (compilation) directory.
  • use different app.config files for different environments.
  • in each app.config configure the right stepassemblies (comment / delete the one that is not needed)

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

I don't need to manually call Assembly.Load because the stepAssembly declaration will pass the reference to NUnit as bindingAssembly (see here ).

If I have both assemblies declared as stepAssemblies, I throw an "ambiguous reference" exception.

We hope that this will help, because the exchange functions are very useful if it is not necessary (when you update your software and perform, for example, tests of a functional script).

+1
source

I think these are really 3 different tests, so I should have 3 sets of function files. However, if you really do not want to go along this route, you can do something like specifying an example table, for example.

 Scenario Outline: Testing app Given I have performed a call to my service using <application> When I do something Then this happens Examples: |Application| | web service | | web application | | direct call | 

The above script will be executed 3 times, passing 3 values. The value will be passed to this method (in this case) so that you can use it to set some context, and then reuse the same step definition files. Then, each step should know whether it is intended to use a web service, a web application, or a direct call.

I would have doubts about this, since they are separate tests, but this is a way to do what you want to achieve.

0
source

I'm not sure what I think about what you want correctly or not, but this may be what you are looking for:

 Scenario Outline: Multiple approaches to test same code Given I am using <ApproachToCallCode> When I do something Then I expect this result Scenarios: Approaches |ApproachToCallCode| |Internal | |WebService | |WebApp | 

Then can you use a conditional expression in this method? I am not sure if this is the best approach, but it should work.

0
source

Steps are not tied to any function, if the steps are the same, or at least some of them, if the wording is the same, or you add an additional attribute to your step definition method, then they can be reused in another script or function.

Depending on the case, I would either write them as an additional script or function.

0
source

Source: https://habr.com/ru/post/1397642/


All Articles