Manually testing Rails REST API with cURL

I was reading REST-esting with cURL and found this very useful for manually testing my Rails JSON APIs. I am using Cucumber for my Behavious-Driven Development but I find that I often need to see how it feels to actually use the API with other tools than the Ruby ones.

I noticed that when running tests, the params Capybara generated in the controller was a Hash, while the cURL-generated params was a String. This was problematic when I was designing a JSON API, as I didn’t want to allow more than one way of posting JSON. Besides, I wanted to make as much use of the Rails magic as possible, so I added to the PostsController a condition for accepting the POST request:
[ruby] # POST /quantities
def create
begin
if params[“post”].is_a? Hash
@post = Post.create(params[:post])
respond_with(@post)
else
respond_with({:error => “Malformed JSON object.”}, :status => 406)
end
rescue
respond_with({:error => “Post Not Created.”}, :status => 400)
end
end[/ruby]

My User model looks like this:
[ruby]class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :token_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable

# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username

# Create the authentication token for the user
before_save :ensure_authentication_token
end[/ruby]
This gives us the ability to accept requests with an authentication token. On the client side, just add a User’s token to the URL: [javascript]?auth_token=token_hash[/javascript] This is a Devise property. The User is :token_authenticatable and therefore have a an authentication_token in the database.

So here’s my Rails magic compatible cURL PUT:
[bash]$ curl -v -H “Accept: application/json” \n
-H “Content-Type: application/json” -X PUT \n
-d ‘{“post”:{“category_id”:1,”confirmed”:false}}’\n
http://localhost:3000/posts/1?auth_token=UeF1pJi5yxHjjgfrjpVf[/bash]
The reason this is compatible is simply the Content-Type HTTP header being specified.

This is how I perform a GET:
[bash]$ curl -v -H “Accept: application/json” \n
-H “Content-Type: application/json” -X GET \n
http://localhost:3000/posts/1?auth_token=UeF1pJi5yxHjjgfrjpVf[/bash]

Now, when I want to test different types of request, I can just replace the MIME types:

  • HTML is text/html
  • XML is application/xml or text/xml
  • JSON is application/json

This form of testing is much faster than running Cucumber for each time I want to see subtle changes. Besides, when I write tests for an API, and I want to prepare a POST test like this:

  Scenario: POST a post should be successful
    Given I send and accept JSON
    When I send a POST request to "/posts" with the following:
      """
        {"post":
          { "category_id":3,
            "date":"2012-02-14 05:00:00",
            "body":"Here's some text for ya!"
          }
        }
      """
    Then the response status should be "201"
    And the newest post should have the following:
      | category_id | body                     | date                |
      | 3           | Here's some text for ya! | 2012-02-14 05:00:00 |

    Given I send and accept XML
    When I send a POST request to "/posts" with the following:
      """
        
          2012-02-14T15:00:00
          1
          Here's some text for me!
        
      """
    Then the response status should be "201"
    And the newest paynote should have the following:
      | category_id | body                     | date                |
      | 1           | Here's some text for me! | 2012-02-14 15:00:00 |

To get the XML structure, I just use my cURL to GET /posts/1.xml or /posts/1.json, then I don’t have to write up the structure for the input myself. 🙂