I ran into a gotcha with my RSpec test for a controller action that returns JSON output. This was one of those cases where a test that always passed suddenly and inexplicably stopped working... Which means that it was just blind luck that made it pass in the first place.

The controller method is very simple:

def index
  render :json => FlashAsset.all.map{|a| {:filename => a.name, :url => a.media.url} }
end

And the corresponding test:

describe 'CRUD GET index' do
  before do
    FlashAsset.destroy_all
    @f1 = FlashAsset.make(:filename => "flash_1.swf")
    @f2 = FlashAsset.make(:filename => "flash_2.swf")
  end
  it 'returns a JSON index' do
    get :index
    response.body.should == [@f1, @f2].map{|f| f.to_json(:only => [:filename, :url])}.to_json
  end
end

When this test failed, here was the result:

'FlashAssetsController CRUD GET index returns a JSON index' FAILED

expected: "[{\"filename\": \"flash_1.swf\", \"url\": \"/flash/1/original/flash_1.swf\"},
{\"filename\": \"flash_2.swf\", \"url\": \"/flash/2/original/flash_2.swf\"}]",

got: "[{\"url\": \"/flash/1/original/flash_1.swf\", \"filename\": \"flash_1.swf\"},
{\"url\": \"/flash/2/original/flash_2.swf\", \"filename\": \"flash_2.swf\"}]" (using ==)

That using == bit was the clue. Notice how logically the contents of the expected and actual results are the same, but the order of the keys is different? JSON is essentially a specially structured string, but a string nonetheless. In order to test this effectively, the JSON has to be interpreted and decoded first.

Once the JSON is decoded, the Array class' comparison operator can handle the rest:

  it 'returns a JSON index' do
    get :index
    _expected = [@f1, @f2].map{|f| f.to_json(:only => [:filename, :url])}.to_json
    # Can't count on the order of the params in the returned JSON...
    ActiveSupport::JSON.decode(response.body) == ActiveSupport::JSON.decode(_expected)
  end
end

Related Articles


Comments

Leave a Comment


IdolHands.com Spam-o-MeterTM
Bot
Spammer
Moron
Human






* Required fields.