Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing Web APIs with Ruby and Cucumber

Testing Web APIs with Ruby and Cucumber

Web apps are hot, web apps with APIs are even hotter. These days popular sites are opening up more and more of their functionality via web APIs and you should too. This talk will cover how to develop web APIs with Ruby, either using Rails or Sinatra. It will also cover how to test those APIs with Cucumber.

Anthony Eden

October 07, 2011
Tweet

More Decks by Anthony Eden

Other Decks in Programming

Transcript

  1. As a developer In order to build rock-solid web APIs

    I will test like a boss Friday, October 7, 11
  2. Feature: create a card As an API client In order

    to add a person to the address book I can create a card Friday, October 7, 11
  3. Scenario: Given I send and accept JSON When I send

    a POST request to "/cards" with the following: ... Then the response status should be "201" And the response body should be a JSON representation of the Card Friday, October 7, 11
  4. Scenario: Given I send and accept JSON When I send

    a POST request to "/cards" with the following: """ { "first_name":"Anthony", "last_name":"Eden", "display_name":"Anthony Eden", "emails":[ { "address_type":"personal", "address":"[email protected]" }, { "address_type":"work", "address":"[email protected]" } ] } """ Then the response status should be "201" And the response body should be a JSON representation of the Card Friday, October 7, 11
  5. Given /^I send and accept JSON$/ do header 'Accept', 'application/json'

    header 'Content-Type', 'application/json' end Friday, October 7, 11
  6. When I send a POST request to "/cards" with the

    following: Friday, October 7, 11
  7. When /^I send a POST request to "([^\"]*)" with the

    following:$/ do |path, body| post path, body end Friday, October 7, 11
  8. Then /^the response status should be "([^"]*)"$/ do |status| begin

    last_response.status.should eq(status.to_i) rescue RSpec::Expectations::ExpectationNotMetError => e puts "Response body:" puts last_response.body raise e end end Friday, October 7, 11
  9. Then /^the response body should be a JSON representation of

    the (\w+)$/ do |model| last_response.body.should eq(model.constantize.last.to_json) end Friday, October 7, 11
  10. Given I have created a card And I store the

    card id to use it in a future API call When I send a GET request to "/cards/{{id}}" Friday, October 7, 11
  11. Given /^I store the card id to use it in

    a future API call$/ do @id = Card.last.id end Friday, October 7, 11
  12. When /^I send a GET request to "([^"]*)"$/ do |path|

    path = Mustache.render(path, {:id => @id}) get path end Friday, October 7, 11
  13. When /^I send a GET request to "([^"]*)" with the

    last (\w+) id$/ do |path, model| id = model.constantize.last.id path = Mustache.render(path, {:id => id}) get path end Friday, October 7, 11
  14. Then /^the first name in the JSON representation of the

    card should be "([^"]*)"$/ do |first_name| json = JSON.parse(last_response.body) match = JSONSelect('.first_name').match(json) match.should eq(first_name) end Friday, October 7, 11
  15. Scenario: create a card with XML Given I send and

    accept XML When I send a POST request to "/cards" with the following: """ <?xml version="1.0"?> <card> <first_name>Anthony</first_name> <last_name>Eden</last_name> <display_ame>Anthony Eden</display_name> </card> """ Then the response status should be "201" And the response body should be an XML representation of the Card Friday, October 7, 11
  16. Given /^I send and accept XML$/ do header 'Accept', 'text/xml'

    header 'Content-Type', 'text/xml' end Friday, October 7, 11
  17. Then /^the response body should be an XML representation of

    the (\w+)$/ do |model| last_response.body.should eq(model.constantize.last.to_xml) end Friday, October 7, 11
  18. "links": [ { "rel": "addresses", "uri": "/cards/1/addresses" }, { "rel":

    "self", "uri": "/cards/1" } ] Friday, October 7, 11
  19. require 'spec_helper' describe "create card" do context "through the API"

    do let(:headers) do { 'HTTP_ACCEPT' => 'application/json', 'HTTP_CONTENT_TYPE' => 'application/json' } end let(:body) do { "first_name" => "Anthony", "last_name" => "Eden", "display_name" => "Anthony Eden", } end it "creates a card" do post '/cards', body.to_json, headers response.status.should eq(201) response.body.should eq(Card.last.to_json) end end end Friday, October 7, 11
  20. it "creates a card" do post '/cards', body.to_json, headers response.status.should

    eq(201) response.body.should eq(Card.last.to_json) end Friday, October 7, 11
  21. How would building services as only APIs affect your choice

    of tools and techniques? Friday, October 7, 11
  22. Are there programming languages that would be better suited to

    API-only services? Friday, October 7, 11