-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Confusion around aliases for {attribute}
and {attribute}_id
#1142
Comments
Just wasted a few hours because of this. The workaround I applied (based on other issues linked and other research) was as below. factory :person
name
email
...
transient do
thing_id { 0 }
thing { nil } # Thing.to_s or similar
end
after :create do |person, options|
person.thing_id = options.thing_id
person.thing = options.thing
person.save!
end
end Feedback on this would be great. Thanks |
Yup, that seems to match the solution in #1096. I think you could use |
Is there a planned "official" solution to this? While hacking module FactoryBot
def self.non_aliasable
%w[processor processor_id]
end
def self.aliases_for(attribute)
aliases.map do |(pattern, replace)|
if pattern.match(attribute.to_s) && !non_aliasable.include?(attribute.to_s)
attribute.to_s.sub(pattern, replace).to_sym
end
end.compact << attribute
end
end This has worked great so far for the project I'm working on, but it doesn't really feel maintainable (as it's a list stored in a module). I'm also not familiar enough with the code to add a method to the DSL to allow defining these on a per model basis but I think that would be the most flexible solution (but may require a rewrite so |
I haven't spent too much time thinking about an official solution for this. I could imagine something like: factory :person do
skip_aliases
# ...
end to opt-out of aliases for just that one factory, but I haven't fully thought that through. In the meantime, you could try using factory :person do
name { "Daniel" }
transient do
thing_id { 0 }
thing { "thing" }
end
initialize_with do
new(attributes.merge(thing_id: thing_id, thing: thing))
end
end And maybe something like factory :person do
skip_aliases :thing, :thing_id
# ...
end to just skip specific aliases for a factory. |
What a nightmare this was to troubleshoot. Count me and a few other coworkers in on wishing factory bot wasn't opinionated on all |
The workarounds posted above help with building/creating, but the attributes are excluded from the I'd really like a way to define these as attributes (which they are), and have factory_bot treat them as plain-text column names if they are not associations. The |
If you have a column name that ends with FactoryBot.define do
factory :domain_account, class: "Domain::Account" do
user
thing_id { SecureRandom.hex(13) }
name { "John Doe" }
sequence(:email_address) { |n| "test+#{n}@example.com" }
end
end causing me an exception that # app/models/domain/account.rb
class Domain::Account < ApplicationRecord
belongs_to :user
self.table_name = "domain_accounts"
end
|
I spent way to much time understanding what was happening... I dont have a suggestion but we definitely need an improvement here! In my case, Adding this to the factory was enough for me:
|
This comment has been minimized.
This comment has been minimized.
We have a Code of conduct that we expect all contributors to follow. |
Just bumping this to say that I just ran into this as well. I have a model that has a {
platform: 'stripe',
platform_id: 'sub_123456789',
}
{
platform: 'apple',
platform_id: 'SOME_APPLE_ITEM_ID',
} I just spent a good 2 hours trying to figure out why this was happening: FactoryBot.build(:product, platform: 'apple', platform_id: 'abc123').attributes.slice('platform', 'platform_id')
=> {"platform"=>"apple", "platform_id"=>nil} |
Two years ago I opened this issue #1512, and now I lost some time investigating why my tests aren't passing 😅. I think a solution could be:
|
In talking with Shopify on the Rails Discord about something different, I learned that they don't use actual foreign keys in the database because they do online schema migrations with 0 downtime. So, it's probably best to simply check the model and see if the field is a foreign key in an association. The whole point of this code is to treat the model = Foo
field = 'bar_id'
foreign_key = model.reflect_on_association(field.delete_suffix('_id'))&.foreign_key
needs_alias = foreign_key.is_a?(Array) ? foreign_key.include?(field) : foreign_key == field If Note: The array check is for Rails 7.1 composite primary keys. |
Is there really no workaround to this? |
@gap777 I was able to work around this issue:
By adding this in the factory: after :build do |product, options|
product.platform_id = options.platform_id
product.platform = options.platform
end |
Grrrr... trying that.. not working for me... |
No, just regular FactoryBot attributes: platform { 'stripe' }
platform_id { "price_#{SecureRandom.base58(24)}" } |
@composerinteralia's suggestion worked for me (#1142 (comment)):
|
We have had several issues over the years related to
FactoryBot.aliases
: #522 #734 #851 #989 #1096 #1138, #1417. In most of these issues people do not realize thatfactory_bot
assumes{attribute}_id
will be the foreign key of an{attribute}
ActiveRecord
association.At the very least I think we need better documentation around this. We could probably also offer better configuration for this, and maybe move anything specific to
ActiveRecord
intofactory_bot_rails
.The text was updated successfully, but these errors were encountered: