Using Hooks

Cucumber supports hooks, which are blocks of code that run before or after each scenario. You can define them anywhere in your support or step definition layers, using the methods Before and After.

To test them, take any Cucumber project and try adding a file features/support/hooks.rb that looks like this:

 
Before ​do
 
puts ​"Go!"
 
end
 
 
After ​do
 
puts ​"Stop!"
 
end

If you run cucumber, you’ll see the two messages printed in the output at the beginning and end of the scenario. Cucumber’s Before and After hooks are a lot like the setup and teardown methods in the xUnit family of testing tools.

The most common use of Before and After hooks is to clear up any residual state left in external systems like databases so that each scenario starts with the system in a known state. We’ll explain this in detail in Chapter 10, Databases.

Hooks are global by default, meaning they run for every scenario in your features. If you want them to run for just certain scenarios, you need to tag those scenarios and then use a tagged hook.

Tagged Hooks

Both Before and After accept a tag expression, which you can use to selectively add the hook to only certain scenarios. For example, suppose you’re writing features for the administrative area of a website. Each of the administrator features starts with the following background:

 
Feature:​ Delete Widgets​
 
 
Background:​​
 
Given ​I am logged in as an administrator
 
 
...

An alternative is to tag the feature and then use a Before hook to run this same code to log in as an administrator.

 
@admin​​
 
Feature:​ Delete Widgets
 
 
...
 
Before(​'@admin'​) ​do
 
user = create_user(:admin => true)
 
login_as user
 
end

Now, to run a scenario logged in as an administrator, you just have to tag the scenario with @admin, and this code will automatically run before the steps of the scenario. In Filtering with Tag Expressions, we will explain more complex tag expressions using logical operations, but a simple tag will do for now.

Tagged hooks can be useful for ensuring technical things like external services are started, without making too much fuss about them in the text of the scenario itself.

Aslak says:
Aslak says:
Step Definitions Are Global

When you define a step definition, it is defined globally. There is no way to reduce the scope of a step definition to certain scenarios like you can do with tagged hooks.

People occasionally ask for a way to scope step definitions in a similar way to tagged hooks, such as making When I turn it off invoke one step definition for some scenarios and another one for others.

This is a feature that would be easy to add to Cucumber, and one day I actually implemented it to get some feedback from the nice people on the Cucumber mailing list. My question was, Can anyone think of how people might misuse this? Richard Lawrence, an old-timer on the list, answered:

Feature-coupled steps is the extreme. The more subtle issue is that the beneficial pressure to grow a ubiquitous language goes away when it becomes too easy to say, “Oh, that’s just another context, I’ll use the same words to mean something different here.” Thinking about some of the conversations I’ve had coaching teams to learn ubiquitous language, I would expect this to happen a lot.

The term “feature-coupled steps” is a term I came up with in the early days of Cucumber when I was documenting good and bad Cucumber practices in the wiki. I consider feature-coupled steps to be a code smell since they quickly cause a lot of duplication and do nothing to promote a ubiquitous language.

When Dan North—the originator of BDD—wrote his first BDD framework, step definitions were coupled to features. He told me the ability to have global step definitions shared across features was one of the improvements Cucumber brought on.

In retrospect, it’s rather amusing to observe the clones and spin-offs of Cucumber re-introducing mechanisms that we deliberately got rid of.

In Cucumber, the same sentence can mean only one thing.

Examining the Scenario

If we want it to, our hook can accept a single argument that represents the scenario. For example, we can ask a scenario for its name:

 
Before ​do​ |scenario|
 
puts ​"Starting scenario ​#{scenario.name}​"
 
end

We can also ask the scenario whether it failed:

 
After ​do​ |scenario|
 
puts ​"Oh dear"​ ​if​ scenario.failed?
 
end

For more details on the Scenario object, look at the documentation for Cucumber::Ast::Scenario.[40]

Around Hooks

Around hooks give you more power than Before and After hooks, allowing you to actually control how many times the scenario is run. An Around hook is passed two parameters: an object that represents the scenario and a block of code that, when called, will run the scenario:

 
Around ​do​ |scenario, block|
 
puts ​"About to run ​#{scenario.name}​"
 
block.call
 
puts ​"Finished running ​#{scenario.name}​"
 
end

With the extra power of Around hooks comes increased responsibility: if you forget to call the block, your scenario won’t run at all.

Around hooks also accept tags, so you can restrict them to run for only some scenarios, just like Before and After hooks. One great trick we’ve seen them used for is to run the same scenario twice under different conditions, like this:

 
Around(​'@run_with_and_without_javascript'​) ​do​ |scenario, block|
 
Capybara.current_driver = Capybara.javascript_driver
 
block.call
 
Capybara.use_default_driver
 
block.call
 
end

Here we’ve used an Around hook that runs only on scenarios that are tagged with @run_with_and_without_javascript. The hook runs the scenario twice, first with Capybara set to use a driver that supports JavaScript and then again with the default driver. You’ll learn more about Capybara itself in Chapter 15, Using Capybara to Test Ajax Web Applications, but this should give you an idea of what’s possible using Around hooks.

Hooks That Run at Other Times

If you want to run code before all of your features start, the way to do that is simply to put it into a Ruby file inside features/support. Any file in there will be run by Cucumber before your first scenario runs.

If you want to run code after all of your features have finished, you can use Ruby’s built-in at_exit hook, which will be run just before the Cucumber process exits. You can write one of these hooks from anywhere, including within a step definition, but you’ll typically use them to tear down some external system that you’ve started from your support code.

There are two other built-in hooks in Cucumber, AfterStep and AfterConfiguration, which we won’t go into here. For more information, refer to the documentation for Cucumber::RbSupport::RbDsl.[41]

Armed with this new knowledge about hooks, let’s get back to work on our ATM.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.218.221