15 Expert Tips for Using Cucumber

By Dave Astels | August 5th, 2009 at 11:08AM

Here we are: the 3rd and final post in my series on Cucumber. Check out the LazyWeb in action: I put out calls for ‘Cucumber Best Practices’ and received an assortment of responses—thanks to everyone who contributed! After collecting and reviewing all the responses, I’ve come up with a list of 15 expert tips you want to keep in mind when working with Cucumber.

In retrospect, I don’t really like the term Best Practice—the value of most practices is very much context dependent. That said, some ways of doing things are just generally better than others, so here are some of those good things to keep in mind. I’ll comment briefly on each one.

1. Feature Files Should Actually be Features, Not Entire Portions of an App

One feature per well named file, please, and keep the features focused.

2. Avoid Inconsistencies with Domain Language

You’ll get the most benefit out of using Cucumber when your customers are involved. To that end, make sure you use their domain language when you write stories. The best course of action is to have them involved in writing the stories.

3. Organize Your Features and Scenarios with the Same Thought You Give to Organizing Your Code

One useful way to organize things is by how fast they run. Use 2-3 levels of granularity for this:

  • Fast: scenarios that run very fast, e.g. under 1/10 of a second
  • Slow: scenarios that are slower but not painfully so, maybe under one second each
  • Glacial: scenarios that take a really long time to run

You can do this separation several different ways (and even some combination):

  • Put them in separate features
  • Put them in separate subdirectories
  • Tag them

4. Use Tags

Tags are a great way to organize your features and scenarios in non functional ways. You could use @small, @medium and @large, or maybe @hare, @tortoise, and @sloth. Using tags let you keep a feature’s scenarios together structurally, but run them separately. It also makes it easy to move features/scenarios between groups, and to have a given feature’s scenarios split between groups.

The advantage of separating them this way is that you can selectively run scenarios at different times and/or frequencies, i.e. run faster scenarios more often, or run really big/slow scenarios overnight on a schedule.

Tagging has uses beyond separating scenarios into groups based on how fast they are:

  • When they should be run: on @checkin, @hourly, @daily
  • What external dependencies they have: @local, @database, @network
  • Level: @functional, @system, @smoke
  • Etc.

5. Use Rake Tasks to Run Features

This provides a consistent environment for running features: this way each run uses the same set of options and parameters. This goes a long way toward maintaining deterministic results.

Another benefit is that this makes for easy integration with continuous integration tools. There is a single point of entry into the spec run, with all options/parameters encapsulated.

6. Don’t Get Carried Away with Backgrounds (Stick to Givens)

The larger the background, the greater the load of understanding for each scenario. Scenarios that contain all the details are self-contained and as such, can be more understandable at a glance.

7. Make Scenarios Independent and Deterministic

There shouldn’t be any sort of coupling between scenarios. The main source of such coupling is state that persists between scenarios. This can be accidental, or worse, by design. For example one scenario could step through adding a record to a database, and subsequent scenarios depend on the existence of that record.

This may work, but will create a problem if the order in which scenarios run changes, or they are run in parallel. Scenarios need to be completely independent.

Each time a scenario runs, it should run the same, giving identical results. The purpose of a scenario is to describe how your system works. If you don’t have confidence that this is always the case, then it isn’t doing its job. If you have non-deterministic scenarios, find out why and fix them.

8. Write Scenarios for the Non-Happy-Path Cases As Well

Happy path tests are easy; edge cases and failure scenarios take more thought and work. Here’s where having some good (and yet pathological) testers on the team can reap rewards.

Use rcov with your full Cucumber runs to find holes in coverage. Definitely check out Aslak Hellesoy’s thoughts on the matter for more details.

9. Be DRY: Refactor and Reuse Step Definitions

Especially look for the opportunity to make reusable step definitions that are not feature specific. As a project proceeds, you should be accumulating a library of step definitions. Ideally, you will end up with step definitions that can be used across projects.

10. Use a Library (Such as Chronic) for Parsing Time in Your Step Definitions

This allows you to use time in scenarios in a natural way. This is especially useful for relative times.

Background:
  Given a user signs up for a 30 day account

Scenario: access before expiry When they login in 29 days Then they will be let in

Scenario: access after expiry When they login in 31 days Then they will be asked to renew

11. Revisit, Refactor, and Improve Your Scenarios and Steps

Look for opportunities to generalize your steps and reuse them. You want to accumulate a reusable library of steps so that writing additional features takes less and less effort over time.

12. Refactor Language and Steps to Reflect Better Understanding of Domain

This is an extension of the previous point; as your understanding of the domain and your customer’s language/terminology improves, update the language used in your scenarios.

13. Use Compound Steps to Build Up Your Language

Compound steps (calling steps from steps) can help make your features more concise while still keeping your steps general—just don’t get too carried away. For example:

Given /^the user (.*) exists$/ do |name|
  # ...
end

Given /^I log in as (.*)$/ do |name| # ... end

Given /^(.*) is logged in$/ do |name| Given "the user #{name} exists" Given "I log in as #{name}" end

14. Use Parallel Step Definitions to Support Different Implementations for Features

For example, running features against Webrat and Selenium. Put these step definitions somewhere where they won’t be auto-loaded, and require them from the command line or rake task.

15. Avoid Using Conjunctive Steps

Each step should do one thing. You should not generally have step patterns containing “and.” For example:

Given A and B
should be split into two steps:
Given A
And B

In Closing…

When I put out a call on Twitter, along with some of the above, I got the following:

  • Sliced, w/tomatoes, red onion, balsamic vinegar, olive oil, salt and pepper.
  • Get the kind without seeds, they stay good longer, and mix with a bit of rice vinegar to play the “flavors that mix well” game
  • slice. add humus. add bread. eat.
  • Eat them freshly picked out of an organic garden!

There are numerous ways to best utilize Cucumber and to improve your general Cucumber practices; the above list is by no means all inclusive. If you’ve got a great tip I missed, leave a comment—I’m always looking to learn and improve :)

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 2% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...

This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

10 Responses to “15 Expert Tips for Using Cucumber”

  1. Luke Melia Luke Melia says:

    Great tips. With regard to parallel step definitons, I think a superior pattern is to use webrat's block helpers:

    webrat.automate do # only gets executed in selenium mode end

    webrat.simulate do # only gets executed in rails integration test mode end

    And many matchers can be written so that they simply work in both environments without any branching at all.

  2. Ben Mabey Ben Mabey says:

    Thanks for another great write-up!

    +1 to Luke's suggestion. I'd also like to point out that using the cucumber CLI with profiles is really the preferred method of running features (with the exception of CI servers) due to the startup time of rake. This wiki page covers more of the reasoning: http://wiki.github.com/aslakhellesoy/cucumber/usi...

    The one other tip I would add to this already great list is concerning tags. In addition to using tags for technical reasons (i.e slow, fast, etc) tags are a great way to help manage your workflow. I like to use @proposed and @in-progress. @in-progress is a great tag to use in conjunction with the –wip flag which will exit with a non-zero status if any of the in-progress features actually pass. Another interesting use of tagging will be limiting the amount of scenarios in a given tag (a'la kanban).. That piece hasn't been worked into Cucumber yet, but you can follow Joe's work on it here: https://rspec.lighthouseapp.com/projects/16211/ti...

  3. Joseph Wilk Joseph Wilk says:

    Great collection of Cuking tips and tasty recipes hints, thanks Dave. We now support limiting the number of certain tags.

    If you have any pending or in-progress tags I recommended limiting the allowed number. This helps prevent your project being flooded with Todos.

    You can read more about limiting tags at Cukes github wiki: http://wiki.github.com/aslakhellesoy/cucumber/tag...

  4. danieroux danieroux says:

    As to step 10 (parsing time):

    Another thing to consider is to always reset time to some know start time before every scenario (I prefer 13 August 2005 – Friday the 13th).

    This is important once you start adding features that act differently over weekends/holidays.

    e.g. if you have a known start time, "interest for the next week, compounding on work days" will always give a predictable answer.

    • cire cire says:

      I wonder if this might limit the thoroughness of the testing. What if the application works great on Fridays, but has a bug when using it on Mondays? I wonder if Chronic addresses weekends directly.

  5. Abe Abe says:

    Can i also recomend that you explore using something prebuilt for saing a lot of time and work like Reuse Step Definitions = Start with Pickle http://github.com/ianwhite/pickle/tree/master

  6. Aslak Hellesoy Aslak Hellesoy says:

    Excellent tips Dave! For more blog links – check out http://wiki.github.com/aslakhellesoy/cucumber/tut...

  7. Andrew Premdas Andrew Premdas says:

    Useful tips, have to disagree with #13 though, I think if you do this a bit you will inevitably get carried away! I would say now never call a step from a step, instead extract a method and put it in a step helper. So to use your example

    module UserStepHelper

    def user_creater(name) …

    def user_login_in(name) …

    end

    World(UserStepHelper)

    Given /^the user (.*) exists$/ do |name|

    user_create(name)

    # … perhaps something here to confirm user creation

    end

    Given /^I log in as (.*)$/ do |name|

    user_login_in(name)

    end

    Given /^(.*) is logged in$/ do |name|

    user_create(name)

    user_login_in(name)

    end

    This will save you much time in future when your "do complex operation" feature that requires and admin user and a customer and … fails.

  8. Very useful tips thanks for sharing this really helped me to generate a better scenario. And also I am looking if you can tell us something on action mailer, like a scenario where a user will sending a multiple mail along with a body containing a activate link.

Leave a Reply