Factory Girl override field defined on after(:build) - ruby-on-rails-3

I can have multiple profiles. All of the have to be associated with the same "api license".
Here is my profile factory:
FactoryGirl.define do
factory :profile do
name "MyString"
after(:build) do |profile|
api_license = ApiLicense.find_by_name('API test name')
api_license ||= FactoryGirl.create(:api_license)
profile.api_license = api_license
end
end
end
I can define multiple profiles with FactoryGirl.build(:profile) or FactoryGirl.create(:profile) and all of them are attached to the same api license, which is good.
The problem comes when I want to test the "no api license case".
For example FactoryGirl.build(:profile, :api_license=>nil) doesn´t work because it seems that after(:build) is executed after assigning nil value to api license.
Any ideas about this?

Create another factory without assigning api license
FactoryGirl.define do
factory :profile_without_api_license, :class => Profile do
name "MyString"
end
factory :profile do
name "MyString"
after(:build) do |profile|
api_license = ApiLicense.find_by_name('API test name')
api_license ||= FactoryGirl.create(:api_license)
profile.api_license = api_license
end
end
end
Call the factory profile_without_api_license whenever you want one without license

Related

FactoryGirl belongs_to with Seeded association

I have a UserType object that ideally is seeded in the DB and remains static:
{id: 1, name: 'Individual'}, {id: 2, name: 'Group'}, {id: 3, name: 'Admin'}
class UserType < ActiveRecord::Base
attr_accessible :name
has_many :users
end
class User < ActiveRecord::Base
attr_accessible :email, :first_name
belongs_to :user_type
end
In testing, I simply want to create an admin user that has its user_type_id field set to 3 when created, and for the UserType.all to have those three items. I've tried a number of things, but here's where I'm at:
FactoryGirl.define do
factory :user_type do
id 1
name "Individual"
trait :group do
after(:create) do |user_type|
id 2
name "Group Leader"
end
end
trait :admin do
after(:create) do |user_type|
id 3
name "Administrative"
end
end
end
end
FactoryGirl.define do
factory :user do
first_name 'TestUser'
email { Faker::Internet.email }
user_type
trait :admin do
after(:create) do |user|
admin_user_type = UserType.where(id: 3).first
admin_user_type = create(:user_type, :admin) unless admin_user_type
user_type admin_user_type
end
end
end
And my test in spec/features/sessions/admin_sign_in_spec.rb:
feature "Admin signing in" do
background do
#institution = create(:institution_with_institutiondomains)
#admin = create(:user, :admin, email: "admin##{#institution.subdomain}.com")
end
scenario "with correct credentials", focus: true do
binding.pry
#admin.inspect
page.visit get_host_using_subdomain(#institution.subdomain)
within("#login-box") { fill_in t('email'), with: #admin.email }
click_button t('session.admin.sign_in') #the action in signing in here checks that user.user_type_id == 3
expect(page).to have_content "You're signed in!"
end
end
In many cases, especially in tests where I have multiple users getting created, I'll receive a MySQL duplicate error on the first id: 1 Individual. I appreciate any guidance.
For what it's worth, anyone finding this may not like my answer, but it is the only thing that works for me. UserTypes are static in my test database, so I removed the traits in the :user_type factory. Instead, I simply set the user_type_id directly and call save on it. Without the save, the change does not persist to my #admin variable. The test data is cleaned between tests using DatabaseCleaner, leaving my user_types table alone.
FactoryGirl.define do
factory :user do
first_name 'TestUser'
email { Faker::Internet.email }
user_type
trait :admin do
after(:create) do |user|
# admin_user_type = UserType.where(id: 3).first
# admin_user_type = create(:user_type, :admin) unless admin_user_type
# user_type admin_user_type
user.user_type_id = 3
user.save #without this, the change won't persist
end
end
end
end

Rails FactoryGirl Duplicated Factory

I have the following factories defined in my factories.rb file:
require 'factory_girl'
FactoryGirl.define do
sequence(:email) {|n| "person-#{n}#example.com" }
factory :country do
...
end
factory :state do
country
...
end
factory :school do
name "Test School"
country
state
end
factory :user do
school
email
...
end
end
When testing in rspec calling FactoryGirl.create(:school) in one of my descriptors causes two schools with the name "Test School" to be created.
I thought the factories defined in factories.rb were just a bunch of unsaved instance objects, can somebody clarify as to why I'm having this issue?
Here's the exact rspec:
require 'spec_helper'
describe "school login" do
it "displays a success message upon successful login to school",do
school = FactoryGirl.create(:school)
user = FactoryGirl.create(:user, :username => "jdoe")
School.all.each do |school|
puts school.name #2x => "Test School"
end
visit school_path(user.school)
click_link('login')
fill_in "username", :with => "jdoe"
fill_in "password", :with => "secret"
click_button "Sign in"
expect(page).to have_selector(".alert-success")
end
end
This line creates the first school
school = FactoryGirl.create(:school)
and this one the second:
user = FactoryGirl.create(:user, :username => "jdoe")
This happens because in your user factory you defined that every user should have a school, so FactoryGirl is creating it for you. If you want your user associated with the first school, you can do something like this:
user = FactoryGirl.create(:user, :username => "jdoe", :school => school)
what's the context code? and how did you find there are 2 schools created?
the code written in ruby files ( factories ) is neither saved to database nor created as object until you declare create(:object) or build(:object).
# Returns a User instance that's not saved
user = FactoryGirl.build(:user)
# Returns a saved User instance
user = FactoryGirl.create(:user)
for more details, refer to : https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

FactoryGirl, 2 times creates user?

I really dont understand of making assotiations. In spec_helper I have got
def log_in_user
user = User.find_by_name 'User1'
user = FactoryGirl.create :user1 unless user
sign_in user
end
in rspec
let(:product) { FactoryGirl.build :product_A }
describe "GET confirm purchase" do
it "xxx" do
log_in_user
Product.should_receive(:find_active_by_id).with("1").and_return(product)
...
end
end
factories.rb
FactoryGirl.define do
factory :user do
encrypted_password 'abcdef1'
confirmed_at Time.now
factory :user1 do
email 'user1#test.com'
name 'User1'
year 1984
end
end
factory :product do
factory :product_A do
name "product A"
association :user, factory: :user1
end
end
end
when I run test case an exception occures:
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
It looks like user1 is creating 2 times, one in log_in_user and the second one in factory: association :user, factory: :user1
I am right? If yes, how can I solve this? I want to create user and have assotiation defined in factory product
best
When you factory :product_A it is automatically calling the factory for :user1.
Then you factory :user1 again in the log_in_user, but the validation on unique emails is preventing the second :user1 from being created.
I would recommend you make email a sequence like so:
FactoryGirl.define do
sequence :email do |n|
"user#{n}#test.com"
end
factory :user do
encrypted_password 'abcdef1'
confirmed_at Time.now
factory :user1 do
email
name 'User1'
year 1984
end
end
factory :product do
factory :product_A do
name "product A"
association :user, factory: :user1
end
end
end
Then, I would alter the sign_in_user to take an (optional) user as an option like this:
def log_in_user(user)
user =|| User.find_by_name 'User1'
user =|| FactoryGirl.create :user1
sign_in user
end
And modify your test case to pass that user object to the login:
let(:product) { FactoryGirl.build :product_A }
describe "GET confirm purchase" do
it "xxx" do
log_in_user(product.user)
Product.should_receive(:find_active_by_id).with("1").and_return(product)
end
end

How can I have associations properly set in Factory Girl?

I am new to FactoryGirl. I come from the fixtures world.
I have the following two models:
class LevelOneSubject < ActiveRecord::Base
has_many :level_two_subjects, :inverse_of => :level_one_subject
validates :name, :presence => true
end
class LevelTwoSubject < ActiveRecord::Base
belongs_to :level_one_subject, :inverse_of => :level_two_subjects
validates :name, :presence => true
end
And I would like to do something like the following in factories:
FactoryGirl.define do
factory :level_one_subject, class: LevelOneSubject do
factory :social_sciences do
name "Social Sciences"
end
end
factory :level_two_subject do
factory :anthropology, class: LevelTwoSubject do
name "Anthropology"
association :level_one_subject, factory: social_sciences
end
factory :archaelogy, class: LevelTwoSubject do
name "Archaelogy"
association :level_one_subject, factory: social_sciences
end
end
end
Then when I use the factory in a spec like this:
it 'some factory test' do
anthropology = create(:anthropology)
end
I get the error:
NoMethodError: undefined method `name' for :anthropology:Symbol
Can anybody help here?
If I do not set the association in factory, then I do not get this error, but I get the error that level_one_subject_id has to be present and only the following test code works:
it 'some factory test' do
social_sciences = create(:social_sciences)
anthropology = create(:anthropology, :level_one_subject_id => social_sciences.id)
end
But I really want to know why the factory with the association does not work. With Fixtures I had all this for nothing.
I think you are trying to group factories by a 'class factory', which is not how FactoryGirl works. It will deduce the ActiveRecord class from the factory name itself, if named appropriately. In case, your factory name is not the same as the class name, we need to explicitly specify the class name using class named parameter. This should work:
FactoryGirl.define do
factory :level_one_subject do # automatically deduces the class-name to be LevelOneSubject
name "Social Sciences"
end
factory :anthropology, class: LevelTwoSubject do
name "Anthropology"
level_one_subject # associates object created by factory level_one_subject
end
factory :archaelogy, class: LevelTwoSubject do
name "Archaelogy"
level_one_subject # associates object created by factory level_one_subject
end
end

How to access the passed-in parameters hash in FactoryGirl

I am working on a web backend in Rails. My Article model is largely a wrapper that delegates most methods to the most recent ArticleVersion. When writing FactoryGirl factories, though, I was trying to create an :article_with_version factory that generates an Article and gives it a version, but I'm not sure how to forward parameters from the Article factory on to the ArticleVersion.
Here is the relevant code:
class Article < ActiveRecord::Base
has_many :versions, :class_name => "ArticleVersion"
def title
self.versions.last.title
end # method title
def contents
self.versions.last.contents
end # method contents
end # model Article
FactoryGirl.define do
factory :article_version do; end
factory :article do; end
factory :article_with_version, :parent => :article do
after_create do |article|
article.versions << Factory(:article_version, :article_id => article.id)
end # after_create
end # factory :article_with_version
end # FactoryGirl.define
What I would like to be able to do is call Factory(:article_with_version, :title => "The Grid", :contents => "<h1>Greetings, programs!</h1>") and have FactoryGirl pass those :title and :contents parameters on to the new ArticleVersion (or nil if those are omitted). Is there a way to access that hash of dynamic parameters that are passed on during Factory.create()?
You can do it using transient attributes like this:
factory :article_with_version, :parent => :article do
ignore do
title nil
contents nil
end
after_create do |article, evaluator|
article.versions = [FactoryGirl.create(:article_version, title: evaluator.title, contents: evaluator.contents)]
article.save!
end
end
Just note that the attributes being ignored will not be set on the Article itself, although it looks like that is the behaviour you want in this case.