Reputation: 21
I am getting this error when using RSpec to test an index action and response in a Rails controller:
JSON::ParserError:
A JSON text must at least contain two octets!
The most common fix -- including render_views
-- is not working and nil
is not being passed in. The test is not hitting the view. When I insert render json: {test: 'hello world'}, status: 200 and return
in the index action of the controller, and a pry in the view (index.json.jbuilder
) and after the get :index
in the test, I can see there is a response body. If I amend the test expectation to expect(response).to render_template '[]'
I can see the empty array that should be in the response body. Why is render_views failing and how to I get it working again?
Here is the index_spec.rb:
require 'rails_helper'
RSpec.describe ThingsController, type: :controller do
render_views
let(:json_response) { JSON.parse(response.body) }
let(:status) { response.status }
let(:user) { create(:user_with_associations) }
subject{ ThingsController }
describe "GET #index" do
context "(success cases)" do
before(:each) do
expect_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
end
context "and when there are no things" do
before(:each) do
get :index
end
it "returns a 200 status" do
expect(status).to eq 200
end
it "returns a top level key of data with an empty array" do
expect(json_response["data"]).to eq []
end
end
end
end
Here is the rails_helper.rb:
ENV["RAILS_ENV"] ||= 'test'
require_relative 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_spec_type_from_file_location!
end
And here is the spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require 'factory_girl_rails'
require 'faker'
include ActionDispatch::TestProcess
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.include FactoryGirl::Syntax::Methods
config.before do
FactoryGirl.factories.clear
FactoryGirl.find_definitions
end
end
And here's the controller action under test things_controller.rb:
class ThingsController < ApplicationController
before_action :authenticate_user!, only: [ :index ]
before_action -> {check_last_updated("Thing")}, only: [ :index ]
def index
@things = @current_user.things.in_last_three_months.approved_and_unapproved.order(start_time: :asc)
end
end
Rails 4.2.0 Ruby 2.1.2 RSpec 3.5.4
This is my first question here, so let me know if there is other information that should be included.
Upvotes: 0
Views: 552
Reputation: 2090
You're not correctly stubbing out the authentication in your spec. You say
expect_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
but you should say
allow_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
As a general rule, *_any_instance_of
methods should be avoided if possible because they can be ambiguous in more nuanced situations. In controller specs, you can use controller
to access the instance of the controller being tested. e.g.
allow(controller).to receive(:set_user_by_token).and_return(user)
Upvotes: 2