I have the following code in my controller:
format.json { render :json => {
:flashcard => @flashcard,
:lesson => @lesson,
Building off of Kevin Trowbridge's answer
response.header['Content-Type'].should include 'application/json'
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.
You could look into the 'Content-Type'
header to see that it is correct?
response.header['Content-Type'].should include 'text/javascript'
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
A lot of the above answers are a bit out of date, so this is a quick summary for a more recent version of RSpec (3.8+). This solution raises no warnings from rubocop-rspec and is inline with rspec best practices:
A successful JSON response is identified by two things:
application/json
Assuming that the response object is the anonymous subject of the test, both of the above conditions can be validate using Rspec's built in matchers:
context 'when response is received' do
subject { response }
# check for a successful JSON response
it { is_expected.to have_attributes(content_type: include('application/json')) }
it { is_expected.to have_attributes(body: satisfy { |v| JSON.parse(v) }) }
# validates OP's condition
it { is_expected.to satisfy { |v| JSON.parse(v.body).key?('success') }
it { is_expected.to satisfy { |v| JSON.parse(v.body)['success'] == true }
end
If you're prepared to name your subject then the above tests can be simplified further:
context 'when response is received' do
subject(:response) { response }
it 'responds with a valid content type' do
expect(response.content_type).to include('application/json')
end
it 'responds with a valid json object' do
expect { JSON.parse(response.body) }.not_to raise_error
end
it 'validates OPs condition' do
expect(JSON.parse(response.body, symoblize_names: true))
.to include(success: true)
end
end
Simple and easy to way to do this.
# set some variable on success like :success => true in your controller
controller.rb
render :json => {:success => true, :data => data} # on success
spec_controller.rb
parse_json = JSON(response.body)
parse_json["success"].should == true