-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in 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: