Hooks vs Backgrounds (Cucumber)

Sometimes you need some pre conditions to run your scenario or a group of scenarios sharing the same steps repeatedly. You can use background or hooks to setup these conditions. How to know what is the best to use ? Well, depends of the case. So today, I will give some examples with best practices when you should use background and when you should use hooks.

Step definition files have a corresponding method available in the before(condition) do . . .method, which has however a matching after(condition) do . . . method as well.

 

Use Background only to setup a pre-condition that the user needs to know

 

If it is not a trivial information to the user, set it up in the implementation (hooks), not in the test steps. Remember feature files should focus on What, and not How. These should be high level steps. We want to keep this simple. So, for this reason you avoid give too many details like this type of steps: “When I press the button”.

 

When using hooks

 

You can use hooks to run before/after each scenario, a group of scenarios according to the tags, all the scenarios in a feature or all the scenarios of your project. They will run before the first step of your scenario, like the background, but it won’t need any step in your feature file.

 

@Before and @After each scenario

Similar to JUnit @Before and @After tagging a method with either of these will cause the method to run before or after each scenario runs. Common functionality like starting or stop browsers are nice to place in these hooks. They reduce the number of common test steps in each scenario. Before hooks will be run before the first step of each scenario. They will run in the same order of which they are registered. After hooks will be run after the last step of each scenario, even when there are failing, undefined, pending or skipped steps.

You may want to finish the tests after the first failure (could be useful in some cases like Continuous Integration when fast feedback is important), for those cases add the command (ruby) in your hook:

  Cucumber.wants_to_quit = true if s.failed?

 

@AfterStep

You have also the possibility to create an after step hook and add for example a take screenshot action. This hook will run after each step of you scenario and you can also filter for certain scenarios using tags. This is only available for Ruby language at the moment, not for Java.

 

@Around

Around hooks will run “around” a scenario. This can be used to wrap the execution of a scenario in a block. The Around hook receives a scenario object and a block (Proc) object. The scenario will be executed when you invoke block.call.

The following example (ruby) will cause scenarios tagged with @fast to fail if the execution takes longer than 0.5 seconds:

Around('@fast') do |scenario, block|
  Timeout.timeout(0.5) do
    block.call
  end
end

 

Tagged hooks

You can filter what are the scenarios that will run this hook every time before start the scenario or after the scenario ends. The condition which enables the before/after block is the tag (false or nil). Tagged hooks can have multiple tags, and follow similar tagging AND/OR rules that the runner does. You can OR and AND tags in much the same way as you can when running Cucumber from the command line.
e.x. @Before(‘@mobile’, ‘˜@login’) for tests needing a mobile browser launched and are not tagged as login

e.x. @Before(‘@mobile, ˜@login’) for tests needing a mobile browser launched or are not tagged as login

 

@Before all scenarios

If you have a hook you only want to run once before all the scenarios, use a global variable. Example (ruby):

Before do 
  $dunit ||= false # define a variable before we can reference its value
  return $dunit if $dunit                  # bail if $dunit TRUE
  step "run the really slow log in method" # otherwise do it.
  $dunit = true                            # don't do it again.
end

 

@AfterConfiguration

You may also provide an AfterConfiguration hook that will be run after Cucumber has been configured. This hook will run only once; after support has been loaded but before features are loaded. You can use this hook to extend Cucumber, for example you could affect how features are loaded or register custom formatters programatically.

 

 

When using background

 

Short Backgrounds

When using background keep it as short as possible. These steps won’t be written out each time the user reads the scenario, so it’s best to have something simple that the user can remember while reading through your feature file.

 

Vigorous Backgrounds

Similar to the above, since these steps won’t be listed with each scenario, the more vivid, the test step is, the easier time the user will have remembering it.

 

Short Feature files

It’s best to keep these feature files smaller, so that the Background information is more readily available. The general rule of thumb is to keep the file small enough to still see the Background test steps at the top of page when reading any scenario.

 

These two methods are powerful tools, but be aware to not use them excessively.

 

Resources:

https://github.com/cucumber/cucumber/wiki/Hooks

https://github.com/cucumber/cucumber/wiki/Cucumber-Backgrounder

https://seleniumbycharan.wordpress.com/2015/08/25/use-of-background-hooks-tags-in-cucumber-jvm/

15 Expert Tips for Using Cucumber

Hey guys, I found these excellent tips to who are working with Cucumber and Calabash. Try follow each one to get more performance and use the best practices always 🙂

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.

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

 

Bye 🙂

Font: https://blog.engineyard.com/2009/15-expert-tips-for-using-cucumber/