RestFul API Testing with Cucumber and Rack Test

API Testing & BDD

API Testing is a nice way to conduct end to end test on your application by communicating with another modules. You can read more about it here

When it comes to Behaviour Driven Development [BDD], API testing become more interesting. I am having great fun experimenting with Cucumber while reading Cucumber Book by Matt Wynne & Aslak Hellesoy. I already tried API Testing with Behat by following Keith Loy ’s blog ‘Testing Rest API with Behat’ which you can read here.

In this short, tutorial, we will see in-process API Testing with Cucumber and Rack Test. We have assumed that Cucumber and Application sits in the same Ruby process. We will be dealing with simple API which stores and returns name of sports and players.

Start with Feature File

We will create simple web Service by using Sinatra and JSON which has /sports route and returns data in JSON format.  Lets Create Project and within that project we will create ‘sports_list.feature’

$ sudo mkdir REST-Cucumber-RackTest
$ cd REST-Cucumber-RackTest
$ vim sport_list.feature

Let’s add following code to the ‘sport_app.rb’ file

Feature: Sports
  In order to know about different sports
  As a sport fan
  I want list of some sports

Scenario: List of the sports

Given the system knows about the following sports::
| name        | player           |
| Cricket     | Sachin Tendulkar |
| Tennis      | Andy Murray      |
| FootBall    | David Beckham    |
When the client requests GET /sports
Then response should be "200"
And the JSON response should be an array with 3 "name" elements
And response should be JSON:
"""
[
  {"name": "Cricket", "player": "Sachin Tendulkar"},
  {"name": "Tennis", "player": "Andy Murray"},
  {"name": "FootBall", "player": "David Beckham"}

]
"""

In here, we are storing some sports and players, then make GET request /sports which should return 200 response. We are also checking number of elements and response body.

Now we will install necessary Gems. Create ‘Gemfile’ with following gems

source 'http://rubygems.org'
gem 'sinatra', '1.3.1'
gem 'json', '1.6.3'

group :test do 
	gem 'cucumber', '1.1.3'
	gem 'rspec', '2.7.0'
	gem 'rack-test', '0.6.1'
end

Install Gems by running command

$ bundle

Now we can run ‘cucumber’ and you will see following

Shashi-MacBook-Pro:REST-Cucumber-RackTest user$ cucumber
/Users/user/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.3.4/lib/bundler/runtime.rb:216: warning: Insecure world writable dir /Users/user in PATH, mode 040777
Feature: Sports
  In order to know about different sports
  As a sport fan
  I want list of some sports

  Scenario: List of the sports                                      # features/sports_list.feature:6
    Given the system knows about the following sports::             # features/sports_list.feature:8
      | name     | player           |
      | Cricket  | Sachin Tendulkar |
      | Tennis   | Andy Murray      |
      | FootBall | David Beckham    |
    When the client requests GET /sports                            # features/sports_list.feature:13
    Then response should be "200"                                   # features/sports_list.feature:14
    And the JSON response should be an array with 3 "name" elements # features/sports_list.feature:15
    And response should be JSON:                                    # features/sports_list.feature:16
      """
      [
        {"name": "Cricket", "player": "Sachin Tendulkar"},
        {"name": "Tennis", "player": "Andy Murray"},
        {"name": "FootBall", "player": "David Beckham"}

      ]
      """

1 scenario (1 undefined)
5 steps (5 undefined)
0m0.018s

You can implement step definitions for undefined steps with these snippets:

Given /^the system knows about the following sports::$/ do |table|
  # table is a Cucumber::Ast::Table
  pending # express the regexp above with the code you wish you had
end

When /^the client requests GET \/sports$/ do
  pending # express the regexp above with the code you wish you had
end

Then /^response should be "([^"]*)"$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

Then /^the JSON response should be an array with (\d+) "([^"]*)" elements$/ do |arg1, arg2|
  pending # express the regexp above with the code you wish you had
end

Then /^response should be JSON:$/ do |string|
  pending # express the regexp above with the code you wish you had
end

In order to make them pass, we need to

  • Create Simple Web service which returns JSON 
  • Organize our Project Code with Step Definitions & Support directory
  • Implement Step Definitions to make them pass

Create a Project Structure by creating files and directory which will look like this :

Project-Structure

GitHub

We have source code available on the GitHub in the repository ‘REST-Cucumber-RackTest‘. You can clone source code and execute following commands to see all the scenarios passing:

$ git clone https://github.com/Shashikant86/REST-Cucumber-RackTest
$ cd REST-Cucumber-RackTest
$ bundle
$ cucumber

 What We have Done?

Let’s try to understand what we have done in each of these files in order to make all steps passed.

sports_app.rb

In this file, we are creating simple web service with route /sports which will return JSON response. We are using Sinatra for creating the web service.

features/support/env.rb

As we are calling Rack-Test’s ‘get’ method, we need to setup ‘environment for the Rack-Test. The ‘get’ method is defined in the in a module called “Rack::Test::Methods”.

Last line of the code in this file

World(Rack::Test::Methods, AppHelper)

This will register two ruby module within Cucumber one for HTTP Simulation and other for own helper module. We have defined AppHelper to communicate between modules.

features/support/hooks.rb

In here, we are clearing data in the ‘before’ hook. We have stored data in the class variable that needs to be cleaned up every time.

features.step_definitions/rest_steps.rb

This is the place where we have implemented all the REST related steps. We have parsed JSON response also we have checked status of the message and number of elements.

features.step_definitions/sports_steps.rb

In this step, we have stored data in the class variable.

At this point, if you run ‘cucumber’ you will see all steps passed.

Shashi-MacBook-Pro:REST-Cucumber-RackTest user$ cucumber
/Users/user/.rvm/gems/ruby-1.9.3-p194/gems/bundler-1.3.4/lib/bundler/runtime.rb:216: warning: Insecure world writable dir /Users/user in PATH, mode 040777
Feature: Sports
  In order to know about different sports
  As a sport fan
  I want list of some sports

  Scenario: List of the sports                                      # features/sports_list.feature:5
    Given the system knows about the following sports::             # features/step_definitions/sports_steps.rb:2
      | name     | player           |
      | Cricket  | Sachin Tendulkar |
      | Tennis   | Andy Murray      |
      | FootBall | David Beckham    |
    When the client requests GET /sports                            # features/step_definitions/rest_steps.rb:2
    Then response should be "200"                                   # features/step_definitions/rest_steps.rb:10
    And the JSON response should be an array with 3 "name" elements # features/step_definitions/rest_steps.rb:14
    And response should be JSON:                                    # features/step_definitions/rest_steps.rb:6
      """
      [
        {"name": "Cricket", "player": "Sachin Tendulkar"},
        {"name": "Tennis", "player": "Andy Murray"},
        {"name": "FootBall", "player": "David Beckham"}
      ]
      """

1 scenario (1 passed)
5 steps (5 passed)
0m0.056s

 

Conclusion

Using Cucumber and Rack Test, we can perform API testing within te same Ruby process. We will see API testing in the ‘Out-of-Process’ in the next post. Stay tuned !!

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>