FactoryGirl ID association - ruby-on-rails

Hello, I'm trying to do test but I have some problems. First, file conn.rb:
FactoryGirl.define do
factory :conn, class: Connection do
from_id 1
to_id 4
end
end
Second file, leg.rb:
FactoryGirl.define do
factory :leg_le, class: Leg do
association :connection_id, factory: :conn
from_id 1
to_id 2
end
end
And my goal is to create leg_le with ID of conn:
conn=FactoryGirl.create(:conn)
leg=FactoryGirl.create(:leg_le)
It seems to create but connection_id is nil.

You may try :
FactoryGirl.define do
factory :connection do
from_id 1
to_id 4
end
end
FactoryGirl.define do
factory :leg do
association :connection, factory: :connection
from_id 1
to_id 2
end
end
then
FactoryGirl.create(:leg).connection_id
# => 1 or whatever your connection_id is
or (if you want to reuse the connection instance elsewhere) :
connection = FactoryGirl.create(:connection)
FactoryGirl.create(:leg, :connection=> connection).connection_id
# => 1 or whatever your connection_id is

Related

Factory fails after changing name

I have a factory that work's fine when its symbol is :notification_event but when I change the name to player_notification_event it fails with the error
uninitialized constant PlayerNotificationEvent.
Also, my other factory :property_notification_event is failing as well, with the error
uninitialized constant PropertyNotificationEvent.
failing factories
factory :player_notification_event do
notification_eventable_type 'Player'
association :notification_eventable, factory: :player
unread_count 1
last_notif_unread_count 0
last_email_message_count 0
last_email_time 5.hours.ago
last_notif_time 3.hours.ago
end
factory :property_notification_event do
notification_eventable_type 'Property'
association :notification_eventable, factory: :property
unread_count 1
last_notif_unread_count 0
last_email_message_count 0
last_email_time 5.hours.ago
last_notif_time 3.hours.ago
end
failing specs
let(:player_notification_event) { create :player_notification_event }
let(:property_notification_event) { create :property_notification_event }
it 'sends email to player' do
player = player_notification_event.notification_eventable
allow(UnreadMessagesMailer).to receive_message_chain(:player_email, :deliver_now!)
described_class.perform
expect(UnreadMessagesMailer).to have_received(:player_email)
end
it 'sends email to property' do
property = property_notification_event.notification_eventable
allow(UnreadMessagesMailer).to receive_message_chain(:property_email, :deliver_now!)
described_class.perform
expect(UnreadMessagesMailer).to have_received(:property_email)
end
passing spec
let(:player_notification_event) { create :notification_event }
it 'sends email to player' do
player = player_notification_event.notification_eventable
allow(UnreadMessagesMailer).to receive_message_chain(:player_email, :deliver_now!)
described_class.perform
expect(UnreadMessagesMailer).to have_received(:player_email)
end
passing factory
factory :notification_event do
notification_eventable_type 'Player'
association :notification_eventable, factory: :player
unread_count 1
last_notif_unread_count 0
last_email_message_count 0
last_email_time 5.hours.ago
last_notif_time 3.hours.ago
end
You can use inheritance here instead of duplicating the entire factory.
[...] it's good practice to define a basic factory for each class with only
the attributes required to create it. Then, create more specific
factories that inherit from this basic parent. Factory definitions are
still code, so keep them DRY.
https://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md
factory :notification_event do
unread_count 1
last_notif_unread_count 0
last_email_message_count 0
last_email_time 5.hours.ago
last_notif_time 3.hours.ago
factory :player_notification_event do
notification_eventable_type 'Player'
association :notification_eventable, factory: :player
end
factory :property_notification_event do
notification_eventable_type 'Property'
association :notification_eventable, factory: :property
end
end
Since the model class is derived from the parent factory :notification_event you don't need to manually specify it.
The default of factory_bot is to look for a class with the same name as first argument in factory, if you don't pass a class explicitly (check official guide). Try this:
factory :player_notification_event, class: NotificationEvent do ...

FactoyGirl: before(:create) actions not being recognized

I am attempting to create my FactoryGirl factories such that when I call FactoryGirl.create(:model_a), any dependency for model_a are created and assigned to that model_a factory. However, for some reason my method is not working and I can't quite figure out why.
In my factory file this is what I have:
FactoryGirl.define do
factory :model_a do
before(:create) do
FactoryGirl.create(:model_b)
end
model_b {ModelB.first}
end
end
Now when I run FactoryGirl.create(:model_a) I would expect this to first create the factory model_b (because of the before(:create) call) and then go back to creating the factory model_a and assigning the factory model_b to the model_b relationshionship for model_a
But instead, I am getting the error model_b must exist, model_b cannot be blank.
Why is the factory model_b not being created so that I can use it?
You need to set the association between model_a and model_b inside your before(:create) block. For example:
FactoryGirl.define do
factory :model_a do
# add model_a attributes as needed
before(:create) do |model_a|
model_a.model_b = ModelB.first || FactoryGirl.create(:model_b)
end
end
end
Or, per OP's comment:
factory :model_a do
# add model_a attributes as needed
model_b { ModelB.first || FactoryGirl.create(:model_b) }
end

has_many relationship using FactoryGirl

I'm trying to create instance with has_many relationship using FactoryGirl factories and fail.
I have two classes: Computers and NetworkCards. Each Computer can have many NetworkCards.
Defined two ActiveRecords:
1.
class Computer < ActiveRecord::Base
has_many :network_cards
end
2.
class Article < ActiveRecord::Base
belongs_to: computer
end
Defined the following factories:
1.
factory :computer do
sequence(:name) { |n| "PC_#{n}" }
transient do
network_cards_count 1
end
after(:create) do |computer|
create_list(:network_card, evaluator.network_cards_count, computer: computer)
done
end
2.
factory :network_card do
sequence(:name) { |n| "NC_#{n}" }
sequence(:type) { |n| "TYPE_#{n}" }
end
Now, when creating a computer in a RSpec test I get a weird behavior I can't explain.
#computer_1 = FactoryGirl.create(:computer)
#computer_1.network_cards.size #Expect 1. Got 0. Why?
#computer_1.reload
#computer_1.network_cards.size # Got 1
Any ideas what am I missing?
Try this:
after(:build) do |computer, evaluator|
computer.network_cards << build_list(:network_card, evaluator.network_cards_count, computer: computer)
end
factory :computer do
sequence(:name) { |n| "PC_#{n}" }
transient do
network_cards_count 1
end
after(:create) do |computer, evaluator|
create_list(:network_card, evaluator.network_cards_count, computer: computer)
computer.reload #<----- solves the issue
end
end

Why is my factory not recognizing my pool association?

we defined the following model
class UserPool < ActiveRecord::Base
belongs_to :pool
belongs_to :user
validates :pool, presence: true
validates :user, presence: true
def self.created(date)
where("DATE(created_at) = ?", date)
end
end
and the following Factroy
FactoryGirl.define do
factory :user_pool do
pool
user
factory :paid_user_pool do
money_on_pool 10
end
end
end
When I run the following test I recive an error
describe "obtain users_pools created at specifict time" do
before do
users = create_list :user, 3, active: false
user4 = create :user
#pool = create :pool
users.each do |user|
create :user_pool, user: user, pool: #pool, created_at: 1.days.ago
end
create :user_pool, user: user4, pool: #pool
end
it "should return just users_pools created at specifict time" do
users_pools = #pool.user_pools.created( 1.days.ago )
users_pools.count.should eq 3
end
end
Error:
ActiveRecord::RecordInvalid:
The validation failed: Pool can’t be blank
Why is my factory not recognizing my pool association?
When creating a factory, you list the attributes with predefined values. Otherwise, you can omit them from the factory and explicitly state it in the test (during create).
# Example for :user
factory :user do
sequence(:name) { |n| "Test User #{n}" }
end
Now, when you call create(:user), the default name will include a number that increases by 1 for every user created. See #sequence and "Sequences" for more information.
Now onto your specific example. You can create the user_pool factory one of two ways.
# No default attributes, requires explicit assignment
factory :user_pool do
end
create(:user_pool, user: user, pool: #pool)
# Default attributes can be overridden during test
# Requires you to create :user and :pool factories
factory :user_pool do
after(:build) do |user_pool|
user_pool.user = create(:user)
user_pool.pool = create(:pool)
end
end
When you build an ActiveRecord object, it is not committed to the database. You are allowed to leave out required attributes. After building the object, two are created (user, pool), and assigned to the correct user_pool attributes. See "Callbacks" in the docs for more information.
If you want to create #pool in your test, you can still do the following. It will override the default pool and user attributes.
user_pool = create(:user_pool, user: user, pool: #pool)

After association callback

Help! I'm in a fix .. check it:
FactoryGirl.define do
factory :card do
number "1234123412341234"
exp_month 12
exp_year 2016
association :user
before(:create) do |instance|
# Start a crypto instance with this users public key and encrypt
crypt = Modules::Crypto.new(instance.user.encryption_key_id)
instance.number = crypt.encrypt("1234123412341234")
end
trait :unencrypted do
number "1234123412341234"
end
end
end
I'm trying to figure out how to:
Trigger a callback after the :user has been created, but before the :card has been created (or the Model validations will fail since the card isn't encrypted)
Make the :unencrypted trait override the callback above.
The trick mentioned in this answer and this issue is to change the create method to save without validating. Then, you can add an after(:create) that encrypts the value.
FactoryGirl.define do
factory :card do
to_create {|instance| instance.save(validate: false) }
number "1234123412341234"
exp_month 12
exp_year 2016
user
after(:create) do |instance|
# Start a crypto instance with this users public key and encrypt
crypt = Modules::Crypto.new(instance.user.encryption_key_id)
instance.number = crypt.encrypt("1234123412341234")
end
trait :unencrypted do
number "1234123412341234"
after(:create) do |instance|
# This is a noop to override previous after(:create)
end
end
end
end
Also note that "if the factory name is the same as the association name, the factory name can be left out."

Resources