Skip to content

Instantly share code, notes, and snippets.

@guenter
Created June 24, 2010 17:55
Show Gist options
  • Save guenter/451730 to your computer and use it in GitHub Desktop.
Save guenter/451730 to your computer and use it in GitHub Desktop.
Snippets for testing REST APIs with Cucumber and friends
Feature: API
In order to use the service from third party apps
As a user
I want to be able to use an API
Background:
Given a user exists # Pickle
And I login as the user using basic auth
Scenario Outline: Get a ticket
Given a ticket exists with title: "The network is down!", description: "Nothing works!" # Pickle
When I GET "/tickets/{the ticket}" using <format>
Then the response code should be 200
And I should see the following ticket in <format> format
| title | description |
| The network is down! | Nothing works! |
Examples:
| format |
| JSON |
| XML |
Scenario: Accept a ticket
Given a ticket exists
When I POST to "/tickets/{the ticket}/accept" using JSON
Then the response code should be 200
And the ticket should be accepted # Pickle
And the user should be the ticket's engineer # Pickle
Scenario: Requeue a ticket to an invalid technology
Given a ticket exists
When I POST to "/tickets/{the ticket}/requeue" using JSON
"""
{"ticket":{"technology":"Wireless Power"}}
"""
Then the response code should be 422
And the ticket's status should not be "requeued" # Pickle
And the response body should be
"""
[["technology","is invalid"]]
"""
def decode_api_response_as(model_name, format)
if format == 'JSON'
decoded = ActiveSupport::JSON.decode(response.body)
if decoded.is_a?(Array)
decoded.map { |inst| inst[model_name] }
else
decoded[model_name]
end
elsif format == 'XML'
decoded = Hash.from_xml(response.body)
decoded[model_name] || decoded[model_name.pluralize]
else
raise "Unsopported response format: #{format}"
end
end
Given /^I login as #{capture_model} using basic auth$/ do |model_name|
user_name = model(model_name).email
password = 't35tp455w0rd'
basic_auth(user_name, password)
end
When /^I (GET|POST|PUT|DELETE)(?: to)? "(.+)" using (JSON|XML)$/ do |method, path, format, *body|
method = method.downcase.to_sym
# Replace {the model} with the corresponding id
path = path.gsub(/\{#{capture_model}\}/) { |match| model!($1).to_param }
format = format.downcase
# HAxx to make this arg. optional
body = body.first
header('Accept', "application/#{format}")
header('Content-Type', "application/#{format}")
visit(path, method, body)
end
Then /^the response code should be (\d+)$/ do |response_code|
response.response_code.should == response_code.to_i
end
Then /^the response body should be$/ do |body|
response.body.should == body
end
Then /^the "([^\"]*)" header should be "(.*)"$/ do |key, value|
response.headers[key].should == value
end
Then /^I should see the following #{capture_plural_factory} in (JSON|XML) format$/ do |plural_factory, format, table|
content_type = "application/#{format.downcase}"
model_name = plural_factory.singularize.gsub(' ', '_')
response_data = decode_api_response_as(model_name, format)
# Check content type
response.content_type.should == content_type
attributes = table.headers
expected = table.rows.sort
actual = response_data.map { |row| row.values_at(*attributes).map(&:to_s) }.sort
actual.should == expected
end
Then /^I should see the following #{capture_factory} in (JSON|XML) format$/ do |singular_factory, format, table|
content_type = "application/#{format.downcase}"
model_name = singular_factory.gsub(' ', '_')
response_data = decode_api_response_as(model_name, format)
# Check content type
response.content_type.should == content_type
attributes = table.headers
expected = table.rows.first
actual = response_data.values_at(*attributes).map(&:to_s)
actual.should == expected
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment