wd113
wd113

Reputation: 507

Specflow test step inheritance causes "Ambiguous step definitions"

I want to have the following test step class structure:

[Binding]
public class BaseStep
{
    [Given(@"there is a customer")]
    public void GivenThereIsACustomer(Table table)
    {
        HandleCustomer(table);
    }

    protected virtual void HandleCustomer(Table table)
    {
    }
}

[Binding]
public class FeatureOneStep : BaseStep
{
    protected override void HandleCustomer(Table table)
    {
         // feature one action
    }

    [Given(@"feature one specific step")]
    public void GivenFeatureOneSpecificAction(Table table)
    {
        // do something
    }

}

[Binding]
public class FeatureTwoStep : BaseStep
{
    protected override void HandleCustomer(Table table)
    {
         // feature two action
    }

    [Given(@"feature two specific step")]
    public void GivenFeatureTwoSpecificAction(Table table)
    {
        // do something
    }
}

"Given there is a customer" is a common step that is used in both FeatureOne and FeatureTwo, but it will have different handling logic inside the two features. So I decide to put this step definition into a base class and override the protected methods in two derived classes respectively.

However, when I ran the tests, I have the following error:

TechTalk.SpecFlow.BindingException: Ambiguous step definitions found for step
'Given there is a customer': 
CustomerTestBase.GivenThereIsACustomer(Table),   
CustomerTestBase.GivenThereIsACustomer(Table)

Can any one tell me how to fix this issue?

Upvotes: 24

Views: 22925

Answers (4)

Mehrdad
Mehrdad

Reputation: 1733

Specflow is a tool for handling the connection between the .feature file and the .cs file. This is not a tool to handle our abstractions. If we need any kind of abstractions or structure to prevent duplication or any other design problem, we can create it separately.

If you consider Specflow as a tool for binding, I am sure that you won't look for a way to handle your abstraction using it.

For example, define an abstract CustomerService

public abstract class CustomerService
{
   public abstract void Handle();
}

Upvotes: 0

florent menanteau
florent menanteau

Reputation: 121

This worked well for me :

public class BaseSteps
{
    [Given(@"Method called")]
    public virtual void WhenMethodCalled()
    {

    }
}    



[Binding]
[Scope(Feature= "specific_feature")
public class DerivedSteps : BaseSteps
{
    [Given(@"Method called")]
    [Scope(Feature= "specific_feature", Tag ="specific_tag")]
    public override void WhenMethodCalled()
    {

    }
}

Upvotes: 12

RunOfTheShipe
RunOfTheShipe

Reputation: 461

Just figuring this out now myself, so a couple of notes (hopefully somebody can use this in the future):

  • Don't include the [Binding] attribute on the base class
  • Create a derived class for each feature file
    • Add the [Binding] attribute to the derived class (will automatically include all step definitions in the base class)
    • Add a [Scope] attribute to the derived class; specify the name of the feature for the named parameter Feature

Upvotes: 35

AlSki
AlSki

Reputation: 6961

The answer is simple; Don't use inheritance to define your bindings.

At run time SpecFlow finds its methods to call by scanning globally across all public classes looking for methods with matching [Given] attributes. This means that you can't have two different implementations for the same Given there is a customer statement, which if you think about it is quite a sensible design decision that will reduce ambiguity.

Upvotes: 13

Related Questions