Book Image

Instant Cucumber BDD How-to

By : Wayne Ye
Book Image

Instant Cucumber BDD How-to

By: Wayne Ye

Overview of this book

<p>Cucumber is a Behavior Driven Design framework, which allows a developer to write specification tests which then tests that the program works as it should. It is a different development paradigm, as it involves writing what the program should do first, then you develop until it passes the tests.<br /><br />Instant Cucumber BDD How-to will cover basics of Cucumber in a Behaviour Driven Development (BDD) style and explain the essence of Cucumber, describe how to write Cucumber features to drive development in a real project, and also describe many pro tips for writing good Cucumber features and steps. Cucumber is a very fun and cool tool for writing automated acceptance tests to support software development in a Behaviour Driven Development (BDD) style.<br /><br />Instant Cucumber BDD How-to will highlight Cucumber's central role in a development approach called Behaviour Driven Development (BDD), describe how to write Cucumber features to drive development in a real project, and finally introduce some famous third-party libraries used inline with Cucumber.</p> <p>It will show you how to carry out all the tasks associated with BDD using Cucumber and write basic Cucumber steps. It will assist you in using Pro tips for writing expressive Gherkin and implement guidelines for writing DRY steps. You'll learn how to use Cucumber's Gherkin to describe the behavior customers want from the system in a plain language.</p>
Table of Contents (7 chapters)

Mastering pro tips for writing good steps (Advanced)


In this recipe, will cover various kinds of tips for writing good, maintainable, and DRY Cucumber steps.

Getting ready

We will reuse the Rails application cucumber_bdd_how_to that we've created in the Writing your first Hello World feature (Simple) recipe, so please cd into that directory to get prepared.

How to do it...

In the following sections, a number of useful step tips will be introduced and covered exhaustively.

Flexible pluralization

  1. Let's imagine that we need to write a step that contains a singular or plural noun depending on its count:

    When the user has 1 gift
    ...
    When the user has 5 gifts
    ...
  2. Instead of implementing two similar step definitions, we can adopt a tip in Cucumber called Flexible Pluralization; the step to match the preceding steps is as follows:

    When /^the user has (\d+) gifts?$/ do |num|
     p num.to_i
    end
  3. Notice the ? (question mark) appended to gifts; it means match zero or more of the proceeding character, and so the step definition will match both gift and gifts.

Non-capturing groups

  1. Sometimes the plural of a noun is irregular, such as person/people, knife/knives. We cannot match them through flexible pluralization, and for these scenarios we need to adopt non-capturing groups, because Cucumber's step statements are eventually treated as regular expressions:

    When there is 1 person in the meeting room
    When there are 8 people in the meeting room
  2. We can define our step as follows:

    When /^there (?:is|are) (\d+) (?:person|people) in the meeting room$/ do |num|
     p num.to_i
    end
  3. By adding a ?: before a normal group, the step will try to match one occurrence of the given word and will not pass the matched value into arguments. Non-capturing groups ensure Gherkin's good readability when dealing with singulars and plurals, and in a DRY manner since one generic step matches various kinds of styles.

Step argument transforms (transform wisdom)

  1. Manually converting a parameter to an integer all the time would be really annoying! We are able to define a step argument transform rule within a step definition file that can be used by other steps:

    Transform /^(-?\d+)$/ do |num|
     num.to_i
    end
  2. The argument transform also supports tables! For example, we have a feature with table input as follows:

    Given this Qatar billionaire has 39 billion
    And his wealth consists of the following major parts
     | Domain      | Worth |
     | Oil         | 21    |
     | Real Estate | 8     |
     | Financial   | 6     |
     | Cargo       | 4     |
  3. We can then define a transform step to convert the table input into any data we want:

    Transform /^table:domain,worth/ do |table|
      table.map_headers! { |header| header.downcase.to_sym }
      table.map_column!(:domain) { |domain| Domain.parse(domain) }
      table.map_column!(:worth) { |worth| "$#{worth}" }
     table
    end

Note

If you want to make the argument transform rule global, create a Ruby file under /step_definitions/support/transform_rules.rb, and put the argument transform step inside it.

Defining the methods and extending "World"

  1. We can define a number of common use methods under the features/support directory, for example creating a current_user method and putting it under features/support/current_user.rb:

    def current_user
     # Code to mock a current user object
    end
  2. We can also utilize Cucumber's World interface to mix in customized modules, for example, we can define an add_headers method:

    module CapybaraHeadersHelper
     def add_headers(headers)
       headers.each do |name, value|
         page.driver.browser.header(name, value)
       end
     end
    end
    World(CapybaraHeadersHelper)
  3. This means that in our step definition we can invoke the add_headers method from the CapybaraHeadersHelper module to add a customized HTTP header when requesting web pages during a test.

Compound steps

Methods can be reused, and so can steps! This is a widely used tip for writing good and DRY Cucumber steps, known as compound steps.

  1. Considering a website provider's ability to log in with third-party accounts such as Facebook, Google, or OpenID, the feature can be described as follows:

    Feature: Login with 3rd party account
    As a website user
    I can login with 3rd party account
    So that I don't have to register a new account
    
     Scenario: Login with Facebook account
       Given user landed at login page
       And he choose login with FacebookThen he should see the Facebook authorization window
    
     Scenario: Login with Google account
       Given user landed at login page
       And he choose login with Google
       Then he should see the Google authorization window
    
     Scenario: Login with OpenID account
       Given user landed at login page
       And he choose login with OpenID
       Then he should see the OpenID login window
  2. The step user choose login with ** can be implemented using the DRY principle as follows:

    Given /^he choose login with (.*)$/ do |account_provider|
     step %{user clicks on the #{account_provider} logo}
     step %{login with #{account_provider}}
    end
    
    Given /^user clicks on the (.*) logo$/ do |account_provider|
     # DOM operation to trigger clicking on the related logo
    end
    Given /^login with (.*)$/ do |account_provider|
     # Implement OAuth login per given 3rd party account
    end

Unanchored steps

  1. In each step's definition, most of the time, the step starts with ^ and ends with $. It looks as follows:

    Given /^user landed at login page$/ do
    end
  2. Both ^ and $ are called "anchors". The preceding step uses ^ and $ to match the string user landed at login page exactly; then we could probably employ "unanchored steps" as follows:

    Then /^wait (\d+) seconds?/ do |seconds|
     sleep(seconds.to_i)
    end
  3. Note at the end of the match we use a question mark, ?, to match the flexible pluralization instead of $, which means all steps containing "wait for x second(s)" will be matched by the preceding step definition, for example:

    When I wait 5 seconds after the certificate has been downloaded 
    When I wait 4 seconds until the loading animation finished

There's more…

Other than the preceding technical tips, the last one, and also the most important tip, is to keep your steps organized!

Be organized

All the preceding tips are targeted at writing maintainable and DRY Cucumber steps. The last tip is to keep Cucumber steps organized, which is kind of a "soft" skill, even though it might be the most important! Categorizing features and step definition files, using tags or hooks, using Rake tasks to encapsulate common running features, and so on; these "rules" are unobtrusive but really important to keep the Cucumber tests maintainable and make daily BDD development life easier.