Cucumber is a very vibrant eco-system on it's own. It became the defacto-standard tool for high-level testing and a great addition to the Behavior Driven Development set of tools. I'm sharing with you a list of tips related to it.
1. Write Declarative Step Definitions
I thank this has become so common-practice, and I totally advocate the decision of removing
web_steps.rb. As you go to the roots of the process itself, it's all meant to be described in a language that's both familiar to your end users and yourself, but also uses dead simple domain terms, as in a token for a conversation.
A good written feature might be written like this.
Scenario: User subscribes to category Given a User exists And is logged in When he subscribes to the "Programming" category Then he should see the posts listed on that category
Perhaps it suffers from a little global abuse but it seems to me that the readability gains outperform that. In order to get the most of this fairly simple feature, and to get the overall picture, you need to understand that:
- We don't care about which User exists, we just want to imply it exists. I don't care about any specific user information in this moment, so don't use it. A generic user factory it's just enough.
- We don't care about the how's in anywhere, we use code for that, the features should have the what's and not the how's.
- Which user is logged in should be implicit, as is here. Why? Because there's no interaction with other user's here, we just don't need it.
- He should see the posts in that category, we've just created it right? We don't care about other categories right now.
2. Use unique actions across you steps codebase
Your actions should be unique. They should be easily distinguishable from any other actions, and they shouldn't be ambiguous. Don't do this, although it might seem pretty trivial.
When /I (order|buy) (.+)/ do |item| # ... end
Although the flexibility might temp you, it'll hurt you later. You're to be expected to have clarity and unambiguity in your feature files.
3. Use and compare only what you need in Tables
Table diffs are a great feature. Just get in mind don't to over-use them. When you're comparing it seems that you can add to readability by cutting the clutter and writing the right step definition.
Background: Given the following Products: | name | price | description | | Socks | 5.00 | ... | | Cap | 3.00 | ... | # ... Scenario: Buying Products # ... When I buy a "Cap" And I buy a pair of "Socks" Then my shopping list should contain: | Socks | | Cap |
Adjust your table diffs to use and compare only relevant information.
We don't care about the price and description in the shopping list, we only want to verify the products are there.
As an aside, it would be really nice if we can avoid the need to prevent repeating the action and do some chain step defining, but it would be strange or abused, I mean I guess there is a good reason this hasn't been implemented. I'm talking about something like this:
# ... Same as before When I buy a "Cap" And a pair of "Socks" # ...
Chain the "buy a..." step definition. What do you think?
4. DRY with Transforms
This is one of the nicest features added to Cucumber. It allows you to avoid the need of repeating the same data transformation from scenario to step definition and instead defining those in a single place. Of course, it could be abused as everything in life, but it's still a life-saver on it's own right.
This simple example it's perhaps the most common example found in my code:
Transform /^\d+/ do |number| number.to_i end
Then, on the step definition (Look mom, no
Then /^I should have (\d+) pets/ do |count| Pets.count.should == count end
5. Use Tagged Hooks
Tagged hooks are a great feature that allows you to run custom code before and after a tagged scenario. It builds upon the normal
After hooks, but now you can use tags with it.
This helps avoiding global abuse and code duplication, along with the added readability.
Suppose you need to perform certain actions if the scenario is for an administrator. You don't want to repeat those actions, and you don't want to call that helper method on every feature/scenario, right?
In the scenario, just a tag away:
@admin Scenario: Whatever you want
And you define the tagged hook:
Before('@admin') do user = Factory.create(:admin) login(user) end
Clean and concise.
6. Use Feature Tags and Subfolders
Cucumber tags are so great that I couldn't help by mentioning them. You should try to use that as much as it makes sense. Subfolders too. Don't try to put everything in a single place when you're only a
require features away.
Get comfortable with the
Integrate your team's workflow with tags as well.
7. Nest Steps with Care
Nest steps carefully, in preference don't nest them at all. Nesting comes with coupling and there are good chances that the benefits are not that huge.
8. Name Feature Branches
Choosing branch names and feature file names is entirely up to you. I use, as many, prefixed feature branches. Setting up your branches name to be as closely as possible to the feature you're working on, and the filename when the feature is described it's a very helpful initiative.
Branch: feature/commenting, Feature: features/commenting.feature
Branch: feature/profile-notifications, Feature: features/profile/notifications.feature
9. Use guard-cucumber
guard-cucumber to run your features for you. Features are slow, I know, but you should do this anyways. It's pretty annoying having your features running that many times, so what about using the Gem and pausing the file modification from time to time? I work this way and I
run my entire suite on every "Pomodoro" break.
10. Take screenshots
No, I'm not telling you how to use PrintScreen or Shutter ;)
That's it. Just a quick list of 10 tips you might heard (or not) about.
Did you think about other interesting tips while you were reading?
Cool, add a comment on them.