Seed-like records in rspec and Factory Girl - ruby-on-rails

I know that seeding test database is a kind of bad idea.
I need to test Stripe api communication in my model tests. I know that external requests in Unit tests is a bad idea too, but nevertheless.
The stripe account has static subscription plan names. So when I test functionality linked with Stripe I need to take them from my database. I use Factory girl gem as a fixture source, but since I need only particular names for Stripe plans I hardcoded them to the factory:
factory :subscription_type do
name "Personal"
frequency "month"
stripe_plan_id "personal"
end
Other factories that uses this one is trying to create each time new object with the same field values and due to validation of uniqueness on the subscription_type model, the factories throw errors. I would prefer if they all use the same record (if exists) if called in one example.
What is the best practice here, guys?

Why not use sequence(:unique_field) {|n| "unique_field #{n}"}, this will make a variable n with order, then field would be unique.

If you'd like to have only one subscription_type object, try creating it first, and passing it as a parameter for the other factories:
#subscription_type = FactoryGirl.create :subscription_type
#other_object = FactoryGirl.create :other_object, subscription_type: #subscription_type
You could also try to define a smart relation for your factory where it checks if there is a subscription type before:
FactoryGirl.define do
factory :user do
subscription_type { SubscriptionType.first.present? ? SubscriptionType.first : FactoryGirl.create(:subscription_type) }
end
end

Related

build multiple objects with a single factory with FactoryGirl

Is it possible to have a factory that is not associated to a specific ActiveRecord model? Instead: the factory's only purpose is to build a bunch of other objects:
# test/factories/address_options.rb
FactoryGirl.define do
factory :address_option do
trait :create_them do
after(:create) do
create(:state)
county = create(:county)
create(:city, county: county)
create(:zip_code)
end
end
end
end
Ex: desired usage would be: create(:address_option, :create_them)
This of course doesn't work because there is no AddressOption class, much less an address_options table. The error I get is:
NameError: uninitialized constant AddressOption
I am aware that I could simply create a trait on one of the factories that are associated to a real activerecord object. But this is a bit different because I am creating a sort of "aggregate" factory: a factory that creates a bunch of objects where some of them are associated to each other while others have no association, but all objects are still related.
Working solution based on suggestion in comments. I am not sure if this is considered "best practice" for using factories, but it is at least a working solution for representing an abstract "aggregate" factory:
# test/factories/aggregates/address_option.rb
class AddressOption
include FactoryGirl::Syntax::Methods
def create_them
create(:state)
county = create(:county)
create(:city, county: county)
create(:zip_code)
end
end
usage: AddressOption.new.create_them

FactoryGirl - overwrite deep related object attribute

I have some not too common issue with factory. I've got several models related with each other. I created quite fine factory that creates that whole "chain" invoking only below
FactoryGirl.create(:application)
Above command creates like I've mentioned before several related models. I user Faker to populate attributes' names. Everything works fine but, I would like to overwrite one deep related model called 'service' with application model. I thought about trait but I can't invoke that trait within
trait :my_trait do
name 'Overwritten name'
end
FactoryGirl.create(:application, :my_trait)
Obviously above is wrong because trait regards to application instead of nested service model. One solution I found is update it after create factory but I would prefer to do it more globally.
In these cases we usually create a separate "lower" instance and pass that to the "upper" instance.
Like:
service = FactoryGirl.build(:service, name: 'Something else')
application = FactoryGirl.create(:application, service: service)
Of course you could also achieve the same behavior if you use this pattern very much in your codebase with the following factory:
factory :application do
transient do
service_name nil
end
association :service
after(:build) do |application, evaluator|
if evaluator.service_name
application.service.name = evaluator.service_name
end
end
end

How can I make all factory girl factories available before evaluating them?

Set up:
We keep each factory in a separate file, and group them in directories to match our models directory structure. We also use mongo and many of our relations are embeded_in rather than typical has_many or belongs_to.
Problem:
When a factory needs to reference another one that is later in alpha order, factory girl throws a Factory not registered error.
Example:
# spec/factories/cat.rb
FactoryGirl.define do
factory :cat do
name 'fluffy'
kittens [FactoryGirl.build(:kitten), FactoryGirl.build(:kitten)]
end
end
# spec/factories/kitten.rb
FactoryGirl.define do
factory :kitten do
name 'mittens'
age_in_months 2
end
end
Since the cat factory is loaded before the kitten factory girl throws the Factory not registered error.
Question
How can I force factory girl to run through all the files and only check the name of the factory the file defines without caring what other factories it depends on?
Yes, I understand I could put the kitten factory in the same file as the cat factory, I also know I could rename kitten.rb to a_kitten.rb to get around this problem. I'd rather not do either of those. Are there any other solutions?
Have you tried this?
# spec/factories/cat.rb
FactoryGirl.define do
factory :cat do
name 'fluffy'
kittens { [build(:kitten), build(:kitten)] }
end
end
Passing a block rather than a value makes it evaluate lazily.

Unable to update attributes of association objects using factory girl and rspec

So, I have a factory that produces a user with a couple of associations. Instead of creating a new factory for each case, I'd like to create the object and modify some attributes of the association objects.
let(:user) do
user = FactoryGirl.create(:user_with_associations)
user.associations.where(some_id: user.default_thing.id).first.some_attribute = Time.now
user.save
user
end
But if I call puts user.associations.to_yaml in an it block, it does not have the updated attribute of some_attribute = Time.now.
What am I missing? Thanks.
A couple of suggestions...
First, you seem to be working against FactoryGirl's strength. A better approach IMHO is to move your customization code into a factory, for example define a FactoryGirl factory for whatever you're building.
Example:
let(:user){ FactoryGirl.create(:user_with_time_now) }
This is really the whole point of FactoryGirl IMHO.
Second, if you're using Rails and ActiveRecord, look at :inverse_of for associations. These tell ActiveRecord that the associations should loop back to the original object.

Rails Factory Girl Associations

I'm trying to create associations with my factories using Factory Girl in a way so that associated objects share the correct data to match them together.
I'm running into scenarios where Object A is related to one instance of Object C, and Object B is related to another instance of Object C, when I'd like them to both be relating to the same instance of Object C.
Factory Girl - https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
Using newest version
Here's a simplified example of what I'm trying to do in my project.
FactoryGirl.define do
factory :neutral_position, class: FinancialAccounting::Update::Neutral::Position do
created_at { Time.now }
updated_at { Time.now }
factory :valid_neutral_position do
association :security, factory: :security
factory :valid_neutral_position_with_production_record do
association :position, factory: :position
# This modifies the created production account record so the attributes match.
FactoryGirl.modify do
factory :position, class: FinancialAccounting::Position do
association :security, factory: :security
end
end
end
end
end
end
Notice I am trying to modify the associated position so that it will point to the same security generated in the :valid_neutral_position factory. This code currently creates two separate securities, so the neutral position is associated with one security, while the position factory is associated with a different security.
How can I achieve this? I see this issue all throughout my application so figuring this out would dramatically improve my app testing suite and keep my code DRY.
It's actually easier to sort this out in your tests/specs rather than factory definitions.
First create a security and assign it to a variable.
Then, when creating an object that you want to depend on that security pass the variable into the factory constructor.
Although your example is a little complex it may look like this:
specific_security = FactoryGirl.create(:security)
valid_natural_position = FactoryGirl.create(:valid_natural_position, security: specific_security)
position = FactoryGirl.create(:position, security: specific_security)
In this way you can ensure the object relationships are set up in your tests the way you need them. It has the added benefit of reducing the number of objects created as the two secondary factories will no longer auto create security objects.
I actually went with a different method. It's contained to the factories, which leads to cleaner tests. However, I do see the downside of having to generate more objects when you might not necessarily want to.
FactoryGirl.define do
factory :neutral_position, class: FinancialAccounting::Update::Neutral::Position do
created_at { Time.now }
updated_at { Time.now }
factory :valid_neutral_position do
has_production_position
end
trait :has_production_position do
association :position, factory: :position
security { position.security }
end
end
end

Resources