how to use traits for associated factories?
Asked Answered
C

2

6

I need to write a test for associated models.

spec/factories/users.rb:

FactoryGirl.define do
  factory :user do
    sequence(:name){ |i| "us#{i}" }
    sequence(:email){ |i| "us#{i}@ad.ad" }
    password 'qwerty'
    password_confirmation{ |u| u.password } 
    info Faker::Lorem.paragraph(7)

    trait :user_status do
      status_id 0
    end

    trait :manager_status do
      status_id 1
    end

    trait :admin_status do
      status_id 2
    end    
  end 
end

but file spec/factories/users.rb is empty.

schema:

create_table "statuses", force: :cascade do |t|
  t.string "title"
end

create_table "users", force: :cascade do |t|
  t.string   "email",                  default: "", null: false
  t.string   "encrypted_password",     default: "", null: false
  t.integer  "status_id",              default: 0
  t.string   "name"  
end

the page index.html.erb contains the below code:

....
...
<div class="status"><%= user.status.title if user.status_id %></div>
....
..

when i run a test:

it 'check response status code for index page' do
  visit users_path
  expect(response).to be_success 
end 

console displays follow error messages:

 Failure/Error: visit users_path
 ActionView::Template::Error:
   undefined method `title' for nil:NilClass

How to write factories for 'user' and 'status' with a trait?

Clearly answered 8/9, 2015 at 18:15 Comment(0)
C
8

eirikir's solution is pretty close but the Status still wouldn't have a title.

Try this:

spec/factories/users.rb

FactoryGirl.define do
  factory :user do
    sequence(:name){ |i| "us#{i}" }
    sequence(:email){ |i| "us#{i}@ad.ad" }
    password 'qwerty'
    password_confirmation{ |u| u.password } 
    info Faker::Lorem.paragraph(7)

    trait :regular do
      association :status, factory: :user_status
    end

    trait :manager do
      association :status, factory: :manager_status
    end

    trait :admin do
      association :status, factory: :admin_status
    end    
  end 
end

spec/factories/statuses.rb

FactoryGirl.define do
  factory :user_status, class: Status do
    title 'User'
  end

  factory :manager_status, class: Status do
    title 'Manager'
  end

  factory :admin_status, class: Status do
    title 'Admin'
  end
end

In your specs, create your User:

create(:user, :regular)
create(:user, :manager)
create(:user, :admin)

or

build(:user, :regular)
build(:user, :manager)
build(:user, :admin)

etc..

Hope that helps!

Concierge answered 8/9, 2015 at 19:55 Comment(0)
W
0

I would make sure that the associated Status instances exist after the build, e.g.:

trait :user_status do
  status_id 0

  after :build do
    FactoryGirl.create(:status, id: 0) unless Status.exists?(id: 0)
  end
end

You could also generalize the creation of the Status instance:

trait :user_status do
  status_id 0
end

trait :manager_status do
  status_id 1
end

trait :admin_status do
  status_id 2
end

after :build do |user|
  if user.status_id && !Status.exists?(id: user.status_id)
    FactoryGirl.create :status, id: user.status_id
  end
end
Winny answered 8/9, 2015 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.