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
Related
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
What is the purpose of transient do in FactoryBot factories?
I've seen a lot of factories that begin with something like below.
factory :car do
owner nil
other_attribute nil
end
...
I've found some information on this blog:
Using FactoryGirl to easily create complex data sets in Rails
But I still don't fully understand how and why to do this. My experience with FactoryBot is minimal.
Could anyone with some experience using FactoryBot share some insight?
transient attributes allow you to pass in data that isn’t an attribute on the model.
Say you have a model called car with the following attributes:
name
purchase_price
model
You want to capitalize the name of the car when you create the car model in the factory. What we can do is:
factory :car do
transient do
# capitalize is not an attribute of the car
capitalize false
end
name { "Jacky" }
purchase_price { 1000 }
model { "Honda" }
after(:create) do |car, evaluator|
car.name.upcase! if evaluator.capitalize
end
end
Hence, whenever you create the car factory and you want to capitalize the name. You can do
car = FactoryGirl.create(:car, capitalize: true)
car.name
# => "JACKY"
Transient attributes are essentially variables local to the factory that do not persist into the created object.
I have seen two main uses of transient attributes:
Controlling/altering the creation of related objects (e.g. accident_count).
Altering values assigned to other attribute assignments (e.g. unsold).
You could, of course, use these transient attributes for anything else you need to code during the object creation.
factory :car do
transient do
accident_count 0
unsold false
end
owner unsold ? 'new inventory' : nil
after(:create) do |car, evaluator|
create_list(:police_report, evaluator.accident_count, vehicle: car)
end
end
This lets your test express a concept (similar to a trait), without knowing anything about the implementation:
FactoryBot.create(:car, make: 'Saturn', accident_count: 3)
FactoryBot.create(:car, make: 'Toyota', unsold: true)
IMO, I would stick with traits when they work (e.g. unsold, above). But when you need to pass a non-model value (e.g. accident_count), transient attributes are the way to go.
I have a factory defined in <Rails-root>/spec/factories/models.rb:
FactoryGirl.define do
factory :model do
id 1
association :organization, factory: :aureso
name "Default Model"
factory :serie_1 do
id 2
name 'serie_1'
end
factory :serie_2 do
id 3
name 'serie_2'
end
factory :serie_3 do
id 4
name 'serie_3'
end
end
end
I want to get all the factories defined for Class Model.
I can get factory definitions for all classes with FactoryGirl.factories, and yes, I can achieve the above using map/reduce. But I want to know if there is any helper method to get all definitions for a model class.
As one can see from the source, the FactoryGirl class in the current version of factory_girl (4.5.0) does not have a convenient way to list the factories that create instances of a given class.
That's not a very exciting answer, so let's do it the inconvenient way! FactoryGirl.factories returns a FactoryGirl::Registry, which again doesn't have a convenient way to list factories but does have an #items hash whose keys are factory names and whose values are instances of FactoryGirl::Factory. The class which a given Factory builds instances of is returned by Factory#build_class. So we can list the factories that build instances of a given class with
FactoryGirl.
factories.
instance_variable_get('#items').
select { |name, factory| factory.build_class == SomeClass }.
keys
Since that relies on internals, it's sure to break in the future.
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
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