Search code examples
ruby-on-railsapiruby-on-rails-4rspecrails-api

RSpec API controllers testing


At first, sorry for my English :)

I need to realize API controller's tests in Ruby on Rails application (v 4.2.0). When I do request to GET Advertising Sources I have a json response like this:

{"advertising_sources":[{"id":59,"title":"accusantium"},{"id":60,"title":"assumenda"}]} 

JSON response template was defined by front-end developer. Now I trying to create tests for: 1. JSON size (2 advert sources) 2. included attributes (id, title)

My tests:

it 'returns list of advertising sources' do
  expect(response.body).to have_json_size(2)
end

%w(id title).each do |attr|
  it "returns json with #{attr} included" do
    hash_body = JSON.parse(response.body)
    expect(hash_body).to include(attr)
  end
end

Failures:

1. Failure/Error: expect(response.body).to have_json_size(2)
   expected {"advertising_sources":[{"id":59,"title":"accusantium"},{"id":60,"title":"assumenda"}]} to respond to `has_json_size?`

2. Failure/Error: expect(hash_body).to include(attr)

   expected {"advertising_sources" => [{"id" => 71, "title" => "necessitatibus"}, {"id" => 72, "title" => "impedit"}]} to include "id"
   Diff:
   @@ -1,2 +1,2 @@
   -["id"]
   +"advertising_sources" => [{"id"=>71, "title"=>"necessitatibus"}, {"id"=>72, "title"=>"impedit"}],

Can anyone help me to correctify my tests code? Thanks!


Solution

  • Given the shape of your response and the characteristics you are interested in testing, you can write your tests as follows:

    describe 'advertising_sources' do
      let(:parsed_response_body) { JSON.parse(response.body) }
      let(:advertising_sources) { parsed_response_body['advertising_sources'] }
    
      it 'returns list of advertising sources' do
        expect(advertising_sources.size).to eq(2)
      end
    
      %w(id title).each do |attr|
        it "returns json with #{attr} included" do
          advertising_sources.each { |source| expect(source.keys).to include(attr) }
        end
      end
    end
    

    I would personally simplify this even further to:

    describe 'advertising_sources' do
      let(:parsed_response_body) { JSON.parse(response.body) }
      let(:advertising_sources) { parsed_response_body['advertising_sources'] }
    
      it 'returns list of advertising sources' do
        expect(advertising_sources.size).to eq(2)
      end
    
      it 'includes an id and title for each source' do
        advertising_sources.each { |source| expect(source.keys).to match_array(%w(id title)) }
      end
    end