4

This is my current testing setup:

# spec/factories.rb
require 'factory_girl'

FactoryGirl.define do
  # Roles

  factory :user_role, :class => Role do
    name 'User'
  end

  # Users
  factory :user, :class => User do
    sequence(:email) {|n| "email#{n}@example.com" }
    password 'password'
    password_confirmation 'password'
    name 'Yuri Userington'
    roles { |a| [a.association(:user_role)] }
  end

  # Instruments
  factory :instrument, :class => Instrument do
    title "Doobie Doo Instrument Title"
    is_valid true
    association :user, :factory => :user
  end

  # Sequences
  sequence :email do
    "email#{n}@factory.com"
  end

end

# spec/controllers/instruments_controller_spec.rb
require 'spec_helper'

describe InstrumentsController do

  before (:each) do
    @instrument = FactoryGirl.create(:instrument)
    @attr = FactoryGirl.attributes_for(:instrument)
    @user = FactoryGirl.create(:user)
  end

  describe "GET index" do
    it "assigns all instruments as @instruments" do
      instrument = Instrument.new(@attr)
      instrument.user = @user 
      instrument.save!
      get :index
      assigns(:instruments).should eq([instrument])
    end 
  end

end

The result is that when i run my tests, i get the following errors in my output:

Failures:

  1) InstrumentsController GET index assigns all instruments as @instruments
     Failure/Error: @instrument = FactoryGirl.create(:instrument)
     ActiveRecord::RecordNotFound:
       Couldn't find Role with id=2
     # ./app/models/user.rb:21:in `assign_role_after_sign_up'
     # ./spec/controllers/instruments_controller_spec.rb:24:in `block (2 levels) in <top (required)>'

Based on that it seems like the roles association call in my :user factory is NOT being called -- what am i doing wrong here? Am i using this in a completely wrong way?

thank you!!

Mario Zigliotto
  • 8,315
  • 7
  • 52
  • 71

2 Answers2

4

There is much to say here. Compare your code with the following to see how many lines or words were removed.

FactoryGirl.define do
  # Sequences
  sequence :email do |n|
    "email#{n}@factory.com"
  end

  # Roles
  factory :user_role, :class => Role do
    name 'User'
  end

  # Users
  factory :user do
    email
    password 'password'
    password_confirmation 'password'
    name 'Yuri Userington'
    roles { |user| [Factory(:user_role)] } #many to many
   end

  # Instruments
  factory :instrument, :class => Instrument do
    title "Doobie Doo Instrument Title"
    is_valid true
    association :user #one-to-one or one-to-many
  end

end

And in your tests:

describe InstrumentsController do

  before (:each) do
    @user = Factory(:user)
  end

  describe "GET index" do
    it "assigns all instruments as @instruments" do
      instrument = Factory(:instrument, :user => @user)
      get :index
      assigns(:instruments).should eq([instrument])
    end 
  end

end

Moreover:

  • I personally prefer testing controller with mocks and stubs

  • I use let instead of instance variables and before_filter

apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • Care to expand on your moreover points? Particularly the "why" behind both of your preferences? Also, thank you very much for the detailed answer/code samples. – Mario Zigliotto Aug 05 '11 at 21:20
  • 1) Testing is about isolating potential troubles so I only use real objects in integration testing. 2) great explanations here: http://stackoverflow.com/questions/5359558/when-to-use-rspec-let 3) Thank you should usually mean +1 on stackoverflow :) – apneadiving Aug 05 '11 at 21:23
  • 1
    One note about this, `roles { |user| [Factory(:user_role)] }` will create a `UserRole` record even if you are only building a `User`. Using the `after_create` callback does not do this. Neither one is right or wrong, it all depends on how you intend to use your factory. It's just something to be aware of. – Wizard of Ogz Aug 05 '11 at 22:21
  • @apneadiving r.e. using mocks/stubs instead of factories, have you found a way to make the test code maintainable? I find I end up having to stub out a lot of methods and the resulting test is hard to read which partially mitigates the benefit to my mind. Maybe I just need to refactor some code into my models, just wondered if you had a view? – Paul Russell Jan 20 '12 at 00:15
  • stubs are fine when you're testing a method which expects results from other objects. It may be a bit more complex to re read but it worth the pain. Actually, I had a case where I relied on a Factory. One day, we decided to change the factory and many tests broked just because there were no more the previously expected data. You have to find your happy medium :) – apneadiving Jan 20 '12 at 00:31
2

I had a similar issues and I used a callback to assign roles like this:

Factory.define :user_with_admin_role, :parent => :user do |user|
  user.after_create {|instance| instance.roles << Factory(:admin_role) }
end

So I think you should be able to do something akin to that:

# Users
factory :user, :class => User do
  sequence(:email) {|n| "email#{n}@example.com" }
  password 'password'
  password_confirmation 'password'
  name 'Yuri Userington'
  after_create {|user| user.roles << Factory(:user_role) }
end

That is completely untested, so you may need to tweak things around.

Wizard of Ogz
  • 12,543
  • 2
  • 41
  • 43