psswf
psswf

Reputation: 21

RSpec test not hitting view with render_view

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

Answers (1)

Glyoko
Glyoko

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

Related Questions