How to check for a JSON response using RSpec?
Solution 1
You can examine the response object and verify that it contains the expected value:
@expected = {
:flashcard => @flashcard,
:lesson => @lesson,
:success => true
}.to_json
get :action # replace with action name / params as necessary
response.body.should == @expected
EDIT
Changing this to a post
makes it a bit trickier. Here's a way to handle it:
it "responds with JSON" do
my_model = stub_model(MyModel,:save=>true)
MyModel.stub(:new).with({'these' => 'params'}) { my_model }
post :create, :my_model => {'these' => 'params'}, :format => :json
response.body.should == my_model.to_json
end
Note that mock_model
will not respond to to_json
, so either stub_model
or a real model instance is needed.
Solution 2
You could parse the response body like this:
parsed_body = JSON.parse(response.body)
Then you can make your assertions against that parsed content.
parsed_body["foo"].should == "bar"
Solution 3
Building off of Kevin Trowbridge's answer
response.header['Content-Type'].should include 'application/json'
Solution 4
There's also the json_spec gem, which is worth a look
https://github.com/collectiveidea/json_spec
Solution 5
You can also define a helper function inside spec/support/
module ApiHelpers
def json_body
JSON.parse(response.body)
end
end
RSpec.configure do |config|
config.include ApiHelpers, type: :request
end
and use json_body
whenever you need to access the JSON response.
For example, inside your request spec you can use it directly
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
get URL, headers: authenticated_header(user)
expect(response).to have_http_status(:ok)
expect(response.content_type).to eq('application/vnd.api+json')
expect(json_body["data"]["attributes"]["email"]).to eq(user.email)
expect(json_body["data"]["attributes"]["name"]).to eq(user.name)
end
end
Related videos on Youtube
Comments
-
Fizz over 3 years
I have the following code in my controller:
format.json { render :json => { :flashcard => @flashcard, :lesson => @lesson, :success => true }
In my RSpec controller test I want to verify that a certain scenario does receive a success json response so I had the following line:
controller.should_receive(:render).with(hash_including(:success => true))
Although when I run my tests I get the following error:
Failure/Error: controller.should_receive(:render).with(hash_including(:success => false)) (#<AnnoController:0x00000002de0560>).render(hash_including(:success=>false)) expected: 1 time received: 0 times
Am I checking the response incorrectly?
-
Fizz about 13 yearsI tried this and unfortunately it says that it got a response of " ". Could this be an error in the controller?
-
Fizz about 13 yearsAlso the action is 'create', does it matter than I use a post instead of a get?
-
zetetic about 13 yearsYes, you'd want
post :create
with a valid parameters hash. -
Robert Speicher about 13 yearsYou should also be specifying the format you're requesting.
post :create, :format => :json
-
tbaums about 13 yearsthis seems a lot easier. Thanks.
-
Kevin Bedell about 12 yearsThis library also includes Cucumber step definitions that lok pretty useful.
-
lightyrs about 12 yearsFor
render :json => object
, I believe Rails returns a Content-Type header of 'application/json'. -
skalee almost 12 yearsJSON is only a string, a sequence of characters and their order matters.
{"a":"1","b":"2"}
and{"b":"2","a":"1"}
are not equal strings which notate equal objects. You should not compare strings but objects, doJSON.parse('{"a":"1","b":"2"}').should == {"a" => "1", "b" => "2"}
instead. -
bricker almost 12 yearsBest option I think:
response.header['Content-Type'].should match /json/
-
CanCeylan over 11 yearsFirst, thanks a lot. A small correction: JSON.parse(response.body) returns an array. ['foo'] however searches for a key in a hash value. The corrected one is parsed_body[0]['foo'].
-
redjohn over 11 yearsJSON.parse only returns an array if there was an array in the JSON string.
-
PriyankaK over 10 yearsJSON.parse(response.body) is returning complete HTML of the page.
-
brentmc79 over 10 years@PriyankaK if it's returning HTML, then your response is not json. Make sure your request is specifying the json format.
-
Dan Garland over 10 yearsrspec-rails provides a matcher for this: expect(response.content_type).to eq("application/json")
-
FloatingRock almost 10 yearsYou could also use
b = JSON.parse(response.body, symoblize_names: true)
so that you can access them using symbols like so:b[:foo]
-
FloatingRock almost 10 yearsCouldn't you just use
Mime::JSON
instead of'application/json'
? -
Edgar Ortega over 9 years@FloatingRock I think you will need
Mime::JSON.to_s
-
webpapaya over 8 yearsLike it because it keeps things simple and doesn't add a new dependency.
-
Franklin Yu over 7 yearsRails 5 made it out of beta, along with
#parsed_body
. It is not yet documented, but at least JSON format works. Note that the keys are still strings (instead of symbols), so one may find either#deep_symbolize_keys
or#with_indifferent_access
useful (I like the latter). -
Philippe Rathé over 4 yearsUse "response.parsed_body". No need to JSON.parse if you "get foo_url, as: :json".
-
Ryan Taylor almost 2 yearseven better
expect(response.content_type).to start_with('application/json')
to avoid the charset attribute that may be present.