Define a Behave step that works for multiple keywords (e.g. Given, When, and Then)

Viewed 6057

Is there a way to write a step that works for multiple keywords. Like say my feature is:

Scenario: Something happens after navigating 
  Given I navigate to "/"
    And say some cookie gets set
  When I navigate to "/some-other-page"
  Then something happens because of that cookie

I'm trying to avoid having to define both:

    @given('I navigate to "{uri}"')
    def get(context, uri):
        current_url = BASE_URL + uri
        context.driver.get(current_url)

    @when('I navigate to "{uri}"')
    def get(context, uri):
        current_url = BASE_URL + uri
        context.driver.get(current_url)

If you only define one and try to use it as both you get a raise NotImplementedError(u'STEP: error. With the above example it's not that bad because the step is simple but it seems like it's bad practice to repeat code and you could have the same thing happening with something more complicated, to me it seems like it would make sense if there was like a @all or @any keyword.

Apologies if this has been answered somewhere but it's a hard thing to search for as it's hard to find unique search terms for this type of question

5 Answers

If you do not want to use @step, you can also do:

@and(u'I navigate to "{uri}"')
 @when(u'I navigate to "{uri}"')
 @given(u'I navigate to "{uri}"')
 def get(context, uri):
     current_url = BASE_URL + uri
     context.driver.get(current_url)

There are conceptual differences between what the @given, @when and @then decorators stand for. You have situations where a step applies to all, some, or only one.

It is all to easy to use @step when the step only applies to 2 situations, and then rely on the test writers to only use the step in the right circumstances. I would encourage people not to do that. Use the following example instead.

@given('step text')
@when('step text')
def step_impl(context):
    pass

However when a step is truly applicable to all cases, a good example is a delay step, then use the @step decorator:

@step('delay {duration} second(s)')
def step_impl(context, duration):
    time.sleep(float(duration))
Related