0

I'm trying to run a test for rendering templates and ran into the error:

undefined method `key?' for 1014:Fixnum

The stub of my model's instance works as it should in my route tests but not so much here. What am I doing wrong?

describe RestaurantsController do
  let(:restaurant) { FactoryGirl.build_stubbed(:restaurant) }

  describe 'GET #show' do
    before { get :show, restaurant.id }

    it { should render_template('show') }
  end
end

Full Error

 1) RestaurantsController GET #show 
     Failure/Error: before { get :show, restaurant.id }
     NoMethodError:
       undefined method `key?' for 1014:Fixnum
     # /Library/Ruby/Gems/2.0.0/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:744:in `html_format?'
     # /Library/Ruby/Gems/2.0.0/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:598:in `process'
     # /Library/Ruby/Gems/2.0.0/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:65:in `process'
     # /Library/Ruby/Gems/2.0.0/gems/devise-3.5.1/lib/devise/test_helpers.rb:19:in `block in process'
     # /Library/Ruby/Gems/2.0.0/gems/devise-3.5.1/lib/devise/test_helpers.rb:72:in `catch'
     # /Library/Ruby/Gems/2.0.0/gems/devise-3.5.1/lib/devise/test_helpers.rb:72:in `_catch_warden'
     # /Library/Ruby/Gems/2.0.0/gems/devise-3.5.1/lib/devise/test_helpers.rb:19:in `process'
     # /Library/Ruby/Gems/2.0.0/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:505:in `get'
     # ./spec/controllers/restaurants_controller_spec.rb:15:in `block (3 levels) in <top (required)>'
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119

1 Answers1

2

get takes a action and hash of params (among other things). It will not implicitly take a model and turn it into { id: model.to_param }.

Instead you need to specify the parameters explicitly.

describe RestaurantsController do
  let(:restaurant) { create(:restaurant) }

  subject { response }

  describe 'GET #show' do
    before { get :show, id: restaurant }
    it { should render_template('show') }
  end
end

As @Зелёный has already mentioned you need to actually persist the record to the database to be able to use it in a controller spec.

To avoid the duplication issue and test ordering issues you should empty the database between each example. The database_cleaner gem is invaluable for that task.

Also if you need to create several records in the same spec you can use sequences in factory girl:

FactoryGirl.define do
  factory :user do
    email { |n| "test-#{n}@example.com" }
  end
end

The gem ffaker is great for generating emails, usernames etc.

Added:

You can setup shortcuts for FactoryGirl by including its methods in your rails_helper:

RSpec.configure do |config|
  # ...
  config.include FactoryGirl::Syntax::Methods
end

This lets you use the FactoryGirl methods without typing the module name like so:

let(:restaurant) { create(:restaurant) }
max
  • 96,212
  • 14
  • 104
  • 165
  • What exactly does `subject { response }` do? – Carl Edwards Aug 01 '15 at 20:40
  • Also, for line two of your answer it needs to be `let(:restaurant) { FactoryGirl.create(:restaurant) }` in order for the test to work. Once that's fixed I'll be more than happy to mark this answer as correct. – Carl Edwards Aug 01 '15 at 20:54
  • Just include the FactoryGirl syntax in your rails helper instead. `subject { response }` sets the subject to be the response object instead of the controller. Its required if you want to write one-liners like `it { should have_http_status :success }` – max Aug 01 '15 at 21:51
  • Thanks so much for your help. – Carl Edwards Aug 01 '15 at 21:54
  • You shouldn't need `subject { response }` -- `render_template` should work on a controller instance (which is the default subject in a controller test). – Elliot Winkler Aug 03 '15 at 17:15