Using factory girl to create HABTM association - ruby-on-rails

I've been trying now for hours to get factorygirl to create two factories - one for users, one for organizations.
But I don't seem to understand how I can reflect a 'has_and_belongs_to_many' relationship in factories, as soon as I try to create an organization and associate it with an admin user, I run into various error messages (depending on the approach I use).
My model seems to be working fine, my seed file populates the dev DB and all the associations are created.
Right now my files look like this:
user factory
FactoryGirl.define do
factory :user do
email 'example#example.com'
password 'password'
password_confirmation 'password'
after(:create) {|user| user.add_role(:user)}
factory :owner do
after(:create) {|user| user.add_role(:owner)}
end
factory :admin do
after(:create) {|user| user.add_role(:admin)}
end
factory :superadmin do
after(:create) {|user| user.add_role(:superadmin)}
end
end
end
Organization factory
FactoryGirl.define do
factory :organization do |f|
f.name "example"
f.website "www.aquarterit.com"
f.association :users, :factory => :admin
end
end
in my specs I test this:
describe Organization do
it "has a valid factory" do
FactoryGirl.create(:organization).should be_valid
end
it "is invalid without a name" do
FactoryGirl.build(:organization, name: nil).should_not be_valid
end
it "is associated with at least one admin user" do
FactoryGirl.create(:organization)
it { should have_and_belong_to_many(:users)}
end
end
all three tests are failing, here are the error message:
1) Organization has a valid factory
Failure/Error: FactoryGirl.create(:organization).should be_valid
NoMethodError:
undefined method `each' for #<User:0x007fadbefda688>
# ./spec/models/organization_spec.rb:7:in `block (2 levels) in <top (required)>'
2) Organization is invalid without a name
Failure/Error: FactoryGirl.build(:organization, name: nil).should_not be_valid
NoMethodError:
undefined method `each' for #<User:0x007fadc29406c0>
# ./spec/models/organization_spec.rb:11:in `block (2 levels) in <top (required)>'
3) Organization is associated with at least one admin user
Failure/Error: organization = FactoryGirl.create(:organization)
NoMethodError:
undefined method `each' for #<User:0x007fadc2a3bf20>
# ./spec/models/organization_spec.rb:15:in `block (2 levels) in <top (required)>'
Any help is as always very much appreciated!
Update
In theory the same thing that works for assigning roles to the user should work for assigning an admin to the organization. But if I change organizations.rb to
FactoryGirl.define do
factory :organization do
name "example"
website "www.aquarterit.com"
after(:create) {|organization| organization.add_user(:admin)}
end
end
I get following error (I do have gem shoulda installed):
1) Organization is associated with at least one admin user
Failure/Error: it { should have_and_belong_to_many(:users)}
NoMethodError:
undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1:0x007ff2395f9000>
# ./spec/models/organization_spec.rb:16:in `block (2 levels) in <top (required)>'

Looks like you are not assigning users correctly and not creating the :admin user properly. For this to work, you need to assign an array of users to organization.users. And, you need to populate that array with a User instance (this assumes you have a User factory named :admin).
factory :organization do
name "example"
website "www.aquarterit.com"
after(:create) {|organization| organization.users = [create(:admin)]}
end

I do it this way, questions and tests have a HABTM relationship so:
FactoryGirl.define do
factory :question do
question 'Some stupid question'
user nil
factory :question_with_test do
# factory_girl's dynamic attributes, ignore it and pass it to evaluator
transient do
test nil
end
after(:create) do |question, evaluator|
create(:question_tests, question: question, test: evaluator.test)
end
end
end
end
Now I can create a question with HABTM to the Test model:
let(:test) { FactoryGirl.create :test, user: user }
let(:question_test_1) { FactoryGirl.create :question_with_test, user: user, test: test }
The question_tests factory is very basic:
FactoryGirl.define do
factory :question_tests, class: 'QuestionTest' do
question
test
end
end

Related

FactoryGirl undefined method `create='

I'm basically getting an error when I'm trying to use a factory I created for a Post model in Ruby on Rails.
Here's the full error:
Failure/Error: post = create(:post)
NoMethodError:
undefined method `create=' for #<Post:0x007fbf1be6e510>
Did you mean? created_at=
# ./spec/models/post_spec.rb:6:in `block (2 levels) in <top (required)>'
Here's the file for the factories:
spec/factories/post.rb
FactoryGirl.define do
factory :post do
title "Hello"
content "Hello, my name is Jacob."
user create(:user)
user_id 1
end
end
spec/models/post_spec.rb
require 'rails_helper'
require 'spec_helper'
describe Post do
it "has a valid factory" do
post = create(:post)
expect(post).to(be_valid)
end
end
I do have a spec/support/factory_girl.rb file which includes the FactoryGirl::Syntax::Methods. This file is loaded by spec/rails_helper.rb.
Also, the create(:user) line works and I'm able to use the user factory in the rails console, but not the post factory.
Any help would be fantastic. Thank you!
In your post factory, you have the syntax wrong for defining an associated record. Try defining it like this:
FactoryGirl.define do
factory :post do
title "Hello"
content "Hello, my name is Jacob."
user
end
end
You just need to state that the post has a user, and you certainly shouldn't be setting them all to have a specific user_id ... since each post will create a new user unless told otherwise and you have no idea what user_id will be generated.

Factory Girl - how to include one association's ID while defining another

A Task belongs to an Employee, who belongs to a User (polymorphic).
Task also has user_id (the same user).
Now how to create this situation using FactoryGirl?
factory :employee do
after(:create) { |employee|
user = FactoryGirl.build :user
user.roleable_type = "Employee"
user.roleable_id = employee.id
user.save!
}
end
factory :task do
employee
user employee.user.id
end
Running a test that creates a task, I get:
/Users/ss/Documents/app/spec/factories.rb:41:in `block (2 levels) in <top (required)>': undefined method `user' for #<FactoryGirl::Declaration::Implicit:0x007fbd67cd2c70> (NoMethodError)

Factory will create one object with a role, but not two

I am using rolify with a User model and a Task model (Rails 4). One of the roles a user can have is "owner." I want to use Factory Girl to create a user object and assign it a role. Here is my factory:
FactoryGirl.define do
factory :task do
owner "Steve"
agency "an agency"
facility "a facility"
description "This task must absolutely be done"
due_date "2013-12-22 03:57:37"
completed_date "2013-12-22 03:57:37"
factory :task_with_owner do
ignore do
user_id nil
end
after(:create) do |task, user_id|
User.find(user_id).add_role :owner, task
end
end
end
end
This spec passes:
it 'is capable of creating a valid object with owner' do
#user = create(:user)
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
end
This spec fails:
it 'is capable of creating two valid objects with an owner' do
#user = create(:user, username: 'janeblow')
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
task = create(:task_with_owner, user_id: #user.id)
expect(#user.has_role? :owner, task).to be_true
end
The error is:
Failure/Error: task = create(:task_with_owner, user_id: #user.id)
ActiveRecord::RecordNotFound:
Couldn't find User with id=#<#<Class:0x000000050f5e10>:0x00000004c9ed08>
# ./spec/factories/tasks.rb:19:in `block (4 levels) in <top (required)>'
# ./spec/models/role_spec.rb:15:in `block (2 levels) in <top (required)>'
Why?
Your after(:create) block looks a little wrong. Try changing it to the following:
after(:create) do |task, vars|
User.find(vars.user_id).add_role :owner, task
end
Then re-run your failing spec.
Because you told the factory to ignore the user_id being passed in and to use nil instead, in your after(:create) block you have to access it from the passed in attributes (in the second block argument, vars in this case). You were almost there, but were passing the object factory_girl uses to hold the attributes rather than the attribute itself from within that object.
See the Transient Attributes section here for another example - https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md

FactoryGirl + Mongoid + RSpec not properly constructing objects.

I am at trying to set up testing in my app, and I have run into a problem with RSpec, FactoryGirl, and Mongoid. I have the following factory:
FactoryGirl.define do
factory :user do |u|
u.name { Faker::Name.name }
u.email { Faker::Internet.email }
u.crypted_password { Faker::Lorem.characters(10) }
u.password_salt { Faker::Lorem.characters(10) }
u.role :user
end
end
I try to use this factory in my tests:
require 'spec_helper'
describe User do
it "has a valid factory" do
create(:user).should be_valid
end
end
But I get this error:
1) User has a valid factory
Failure/Error: FactoryGirl.create(:user).should be_valid
NoMethodError:
undefined method `user' for #<User:0x007ff24a119b28>
# ./spec/models/user_spec.rb:5:in `block (2 levels) in <top (required)>'
I don't know what is causing this error. Also, is there a way to see a full stacktrace using rspec?
This line has problem
u.role :user
I guess you want to define a default role as "user"? Then don't use symbol or method, use string instead
u.role 'user'

Setting roles through rolify in FactoryBot definition

I am using devise, rolify and cancan. I'm also using RSpec with FactoryBot for testing. Right now I'm working on some tests and I want to define users with different roles for those tests. Here is my current guess for how to do that using FactoryBot:
FactoryBot.define do
factory :user do
name 'Test User'
email 'example#example.com'
password 'please'
password_confirmation 'please'
# required if the Devise Confirmable module is used
confirmed_at Time.now
factory :admin do
self.has_role :admin
end
factory :curator do
self.has_role :curator
end
factory :super_admin do
self.has_role :super_admin
end
end
end
Here are some of my tests which don't seem to be written correctly:
require 'spec_helper'
describe "Pages" do
subject { page }
before do
#newpage = FactoryBot.create(:page)
#newpage.save
end
context 'not logged in' do
it_behaves_like 'not admin'
end
context 'logged in' do
context 'as user' do
before do
#user = FactoryBot.create(:user)
sign_in #user
end
it_behaves_like 'not admin'
end
context 'as curator' do
before do
#curator = FactoryBot.create(:curator)
sign_in #curator
end
it_behaves_like 'not admin'
end
context 'as admin' do
before do
#admin = FactoryBot.create(:admin)
sign_in #admin
end
it_behaves_like 'admin'
end
context 'as super admin' do
before do
#super_admin = FactoryBot.create(:super_admin)
sign_in #super_admin
end
it_behaves_like 'admin'
end
end
end
When I run the specs I these are my errors:
1) Pages logged in as admin behaves like admin can show page
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `has_role=' for #<User:0x007f883384d178>
Shared Example Group: "admin" called from ./spec/requests/pages_spec.rb:41
# ./spec/requests/pages_spec.rb:37:in `block (4 levels) in <top (required)>'
2) Pages logged in as curator behaves like not admin can show page
Failure/Error: Unable to find matching line from backtrace
ArgumentError:
Factory not registered: curator
Shared Example Group: "not admin" called from ./spec/requests/pages_spec.rb:32
# ./spec/requests/pages_spec.rb:28:in `block (4 levels) in <top (required)>'
3) Pages logged in as super admin behaves like admin can show page
Failure/Error: Unable to find matching line from backtrace
ArgumentError:
Factory not registered: super_admin
Shared Example Group: "admin" called from ./spec/requests/pages_spec.rb:50
# ./spec/requests/pages_spec.rb:46:in `block (4 levels) in <top (required)>'
Note: FactoryBot was previously called FactoryGirl
I would rather use FactoryBot's after(:create) callback to create roles (also see this issue for Rolify).
Furthermore the method has_role? is used to check if a user has a specific role, to set a specific role you should use the add_role method.
FactoryBot.define do
factory :user do
name 'Test User'
email 'example#example.com'
password 'please'
password_confirmation 'please'
# required if the Devise Confirmable module is used
confirmed_at Time.now
factory :admin do
after(:create) {|user| user.add_role(:admin)}
end
factory :curator do
after(:create) {|user| user.add_role(:curator)}
end
factory :super_admin do
after(:create) {|user| user.add_role(:super_admin)}
end
end
end
Note: FactoryBot was previously called FactoryGirl

Resources