In factory definition, assign attribute using attribute of other factory - ruby

I have two factory_girl factories, contact and user. Contact has an attribute, dest_user_id, which is a foreign key to user, but can be NULL. For that attribute, I want to create a new user using the user factory, and then assign its id to dest_user_id. Is there a way to do that?

Having a foreign key of dest_user_id shouldn't change the approach, provided the relationship remains the same as the model name. Assuming that contact belongs_to user and a user either has_one or has_many contacts, you can achieve what you want as follows:
First create the factories:
FactoryGirl.define do
factory :user do
Field 1
Field 2
# etc
end
factory :contact do
# Add only the minimum fields required to create a contact, but not the association
Field 1
Field 2
factory :contact_with_user do
# then you add the association for this inherited factory
# we can use 'user' here as the factory is the same as the association name
user
end
end
end
With this setup you create a contact without a user, and therefore dest_user_id is NULL when you user FactoryGirl.create(:contact) within your test/spec.
To create a user and assign the id into the dest_user_id field on the contact you would use the following:
#user = FactoryGirl.create(:user)
#contact = FactoryGirl.create(:contact_with_user, :user => #user)
This approach maximises flexibility as you can create a contact with or without a user, and you can have the user.id passed into the contact model only if you need it for a particular test. If you call FactoryGirl.create(:contact_with_user) then both a contact and a user will automatically be created but you wouldn't have the control over the assignment of dest_user_id that you have when you use FactoryGirl.create(:contact_with_user, :user => #user)

Related

how to create factory that passes override through associations

class User < ActiveRecord::Base
belongs_to :account
end
class Account < ActiveRecord::Base
has_many :users
has_many :reminders
end
class Reminder < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
factory :user do
account
end
factory :reminder do
account
user
end
How do I modify the :reminder factory so that I can pass in an account and that account will be used for both the Reminder as well as the User?
r = FactoryBot.create(:reminder, account: Account.last)
r.account.id == r.user.account.id
You can pass a block to factory attribute definitions if the default does not suit you. In the current case, you want to make sure that the account and user match.
factory :reminder do
account
user { build(:user, account: account) }
end
This will ensure that the same account is used for the user if the factory needs to build the user. This will cover your requirement. However, there is another case that is not covered. If you pass in a user, but no account, then a new account will be built and the accounts will not match. Checking for a given user can mitigate this.
#overrides has the attributes that are passed in.
factory :reminder do
account { #overrides[:user]&.account || build(:account) }
user { build(:user, account: account) }
end
Now if only user or only account is passed into the factory, then the resulting reminder will have accounts matching.
Try FactoryBot.build it will build models, with defined associations, but those should be replaced later when you assign them to account on the last line:
r = FactoryBot.build(:reminder)
u = FactoryBot.build(:user)
a = FactoryBot.create(:account, users: [u], reminders: [r])

Creating multiple unique entries with transient FactoryGirl

I have a FactoryGirl Object that creates a Category in my case (it is associated with an image)
class Image < ActiveRecord::Base
has_many :image_categories, dependent: :destroy
has_many :categories, through: :image_categories
validates :categories, presence: { message: 'Choose At Least 1 Category' }
end
class Category < ActiveRecord::Base
has_many :image_categories
has_many :images, through: :image_categories
validates :name, presence: { message: "Don't forget to add a Category" }
validates_uniqueness_of :name, message: 'Category name %{value} already exists'
end
FactoryGirl.define do
factory :category do
name 'My Category'
end
end
FactoryGirl.define do
factory :image do
title 'Test Title'
description 'Test Description'
transient do
categories_count 1
end
categories { build_list(:category, categories_count) }
end
end
When creating an image with 1 category everything is fine, but if i try and save with 2 categories the second entry gets saved as nil, I guess thats because of my validation of unique names.
So my question is how can I use transients to create a list of 2 unique categories
Hope this makes sense
Thanks
Direct from Factory Girl Documentation: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Transient_Attributes
FactoryGirl.define do
# post factory with a `belongs_to` association for the user
factory :post do
title "Through the Looking Glass"
user
end
# user factory without associated posts
factory :user do
name "John Doe"
# user_with_posts will create post data after the user has been created
factory :user_with_posts do
# posts_count is declared as a transient attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
posts_count 2
end
# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including transient
# attributes; `create_list`'s second argument is the number of records
# to create and we make sure the user is associated properly to the post
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
end
end

Factory Girl - creating a factory with active record validations

So I have a factory to create an item. Which can contain one or more line_items.
describe Item do
let(:user) { create :user }
let(:currency) { create :currency }
subject(:item) { create :item, currency: currency, create_user: user, update_user: user }
It fails when creating the item with an active record validation:
ActiveRecord::RecordInvalid: Validation failed: You have not entered any data and cannot save a blank form., You have not entered any data and cannot save a blank form.
Is this because a line_item is not being created when an item is created?
In my factory, whenever I create a line_item, I create an item:
FactoryGirl.define do
factory :line_item do
item
create_user factory: :user
update_user factory: :user
end
end
Make Sure that your models are validated by your model validations in your Factory. Please can you paste your models. Try the code below. It is not tested so you have to doctor it based on your model.
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "foo#{n}"}
password "password"
email {"#{username}#example.com"}
end
factory :item do
name "item1"
user
end
factory :line_item do
name "line_item1"
item
end
end

Rails 4 Active Admin, Attribute Won't Save

I'm currently using rails 4 and active admin for my admin interface. I have a profile which has many analytics_by_weeks.
class Profile < ActiveRecord::Base
has_many :analytics_by_weeks
I want to use active admin to create new analytics_by_weeks and link them to a profile (following my belongs_to has_many relationship), but when I click create to generate the new analytics_by_week the profile attribute will not save.
In my program I added a name attribute to a profile:
add_column :profile, :name, :string
I have tried making a custom formtastic form:
f.input :profile, :collection => Profile.all.map{ |p| [p.name, p.id] }
Nothing has worked, what am I doing wrong? I have also used permit parameters to permit profile, id, and name.

Do not autosave belongs-to association

I have a model Subscription with a belongs-to association to Participant.
The subscription form uses fields_for to build the associated participant fields.
Also in the form is a radio button called called 'other_person'.
What I want is to NOT save the associated participant table (so also not validate) when the other_person field is set to false.
I will assume other_person is a field of the Subscription model on the following example:
class Subscription < ActiveRecord::Base
before_save :remove_empty_participant
belongs_to :participant
private
def remove_empty_participant
self.participant = nil unless self.other_person
end
end
If it's not a field of your Subscription model, you will have to remove the attributes in the controller's action:
class SubscriptionsController < ActionController
def create
params[:subscription].delete(:participant) unless params[:other_person]
# Save the subscription with your current params...
end
end
Hope it helps.

Resources