5

I am just learning Cucumber and notice that if two completely seperate features have two steps that are accidentally worded the same, Cucumber suggests only one step definition for them. Does this mean that step definitions are global and they are meant to be shared?

Example

Suppose a team of business analysts is writing specs for a financial firm that has a banking division and a brokerage division. Further assume that two different people are writing features for their respective divisions to calculate transaction fees.

The banking guy writes:

Feature: Transaction Fees
    Scenario: Cutomer withdraws cash from an out-of-netwrok ATM
        Given that a customer has withdrawn cash from an out-of-netwrok ATM
        When I calculate the transaction fees
        Then I must include an out-of-netwrok ATM charge

The brokerage guy writes

Feature: Transaction Fees
    Scenario: Cutomer places a limit order
        Given that a customer has placed a limit order
        When I calculate the transaction fees
        Then I must include our standard limit-order charge

Note that the When clause is the same for both the scenarios. Even worse, both guys put this scenario in a file called transaction-fees.feature (in different directories of course).

Cucumber produces the following recommendation for step definitions:

You can implement step definitions for undefined steps with these snippets:

this.Given(/^that a customer has withdrawn cash from an out\-of\-netwrok ATM$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.When(/^I calculate the transaction fees$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.Then(/^I must include an out\-of\-netwrok ATM charge$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.Given(/^that a customer has placed a limit order$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

this.Then(/^I must include our standard limit\-order charge$/, function (callback) {
  // Write code here that turns the phrase above into concrete actions
  callback.pending();
});

Note that the when clause is suggested only once.

  1. Does this mean that there needs to be only one step definition that needs to be entered in only one of the two step definition files?
  2. Does cucumber associate feature files with similarly named step_definition files? In other words, does it associate transaction-fees.feature with transaction-fees.steps.js? If all the step definitions are global, then I might mistakenly assume that file/directory setup is just for organization and doesn't mean anything as far as the execution envirnment goes.

Thanks in advance for your time and clarifications.

Naresh
  • 23,937
  • 33
  • 132
  • 204

1 Answers1

3

step defs are attached to the World object, which is "this" in your code above.

  1. There should only be one step definition. They are meant to be shared. IIRC, the Cucumber Boo, page 149 (https://pragprog.com/book/hwcuc/the-cucumber-book) gets into the details of this design decision. Although it is ruby, I think this is the same across all cucumber implementations.

  2. Cucumber does not associate feature files and step_definition files. The file tree/convention is for convenience only.

Jeff Price
  • 3,229
  • 22
  • 24
  • 2
    Thank you - very clear now! Makes me wonder then how to scale this up on large projects without tripping over each other. – Naresh Oct 03 '14 at 21:05
  • 1
    By being very intentional with your gherkin :) In the example you provide, I would consider a conflict to be a possible code smell. Why is your ATM code and your brokerage code living in the same code base being tested by the same test suite? I would think they would be very different systems. – Jeff Price Oct 04 '14 at 04:33
  • 1
    However, maybe this is a unified billing app that looks at the transactions and is compiling a single statement for a customer, in which case maybe the conflict isn't a conflict at all. The Given sets up a "transaction" object, the When calls calculate_fee() on that and returns a set of line items, the "Then" checks to see the appropriate line item is found. In this case, you can have the exact same "When I calculate the transaction fees" as well as the same "Then I must include (\w+) charge". So in fact, you aren't tripping, you are enforcing your api and design. – Jeff Price Oct 04 '14 at 04:36
  • Awesome! Thanks again for your thoughtful comments. – Naresh Oct 04 '14 at 14:45