Search code examples
ruby-on-rails-3testingrestcucumberrspec-rails

Rack::Test PUT method with JSON fails to convert JSON to params


I am currently using rails 3.1.3, cucumber-rails 1.2.1, rspec-rails 2.8.1, and json_spec 0.8.0. I am building a restful web service API that will be used by an Android app. I am using TDD / BDD practices and have been able to successfully test web service actions with get() and post() in my Cucumber step definitions. However, when using put(), I ran into an issue.

Here is my device_update.feature:

Feature: Updating a Device
  The web service should accept a PUT request to update device settings.

  Scenario: The device information is updated when valid data is posted to the web service.
    Given a device exists
    When I put "/services/devices/4A3CABD4C4DE6E9F.json" with:
      """
      {
        "device": {
          "carrier": "SPRINT"
        }
      }
      """    
    Then the JSON should be:
      """
      {
        "message": "Device updated.",
        "response_code": 1
      }
      """

My step definition is:

When /^I get "([^"]*)"$/ do |path|
  get(path)
end

When /^I post to "([^"]*)" with:$/ do |path, json_string|
  post(path, json_string, {"CONTENT_TYPE" => "application/json"})
end

When /^I put "([^"]*)" with:$/ do |path, json_string|
  put(path, json_string, {"CONTENT_TYPE" => "application/json"})
end

When the test is run, I get the following:

When I put "/services/devices/4A3CABD4C4DE6E9F.json" with:
  """
  {
    "device": {
      "carrier": "SPRINT"
    }
  }
  """
  You have a nil object when you didn't expect it!
  You might have expected an instance of Array.
  The error occurred while evaluating nil.[] (NoMethodError)
  ./app/controllers/services/devices_controller.rb:75:in `update'
  ./features/step_definitions/request_steps.rb:10:in `/^I put "([^"]*)" with:$/'
  features/services/devices/device_update.feature:7:in `When I put "/services/devices/4A3CABD4C4DE6E9F.json" with:'
Then the JSON should be:
  """
  {
    "message": "Device updated.",
    "response_code": 1
  }
  """

When I look at the log, I see the following:

Started PUT "/services/devices/4A3CABD4C4DE6E98.json" for 127.0.0.1 at 2012-01-25 11:36:16 -0800
Processing by Services::DevicesController#update as JSON
Parameters: {"{\n  \"device\": {\n    \"carrier\": "SPRINT"\n  }\n}"=>nil, "id"=>"4A3CABD4C4DE6E98"}

I truncated the active record calls as they are not relevant. It's obvious that the JSON is not being converted into proper parameters and this is why I'm getting an unexpected nil.

Any thoughts on how to make this work?


Solution

  • You should not pass a string to put. It takes care of the conversion into a JSON string, which is generally a good thing. What is happening here is that your JSON string is JSONified again by put.

    You should parse the JSON string first and pass the resulting object to put:

    When /^I put "([^"]*)" with:$/ do |path, json_string|
      payload = JSON.parse(json_string)
      put(path, payload, {"CONTENT_TYPE" => "application/json"})
    end
    

    Also the content-type is probably unnecessary.