英文:
FactoryBot ActiveRecord::AssociationTypeMismatch error with wrong class
问题
I am working on a proof of concept Rails application after a long break from using Rails. I set up RSpec tests, as well as FactoryBot and Faker to generate test data. In my app, I have two models:
class Admin::Tenant < ApplicationRecord
has_rich_text :description
has_and_belongs_to_many :users,
association_foreign_key: :admin_user_id,
foreign_key: :admin_tenant_id
has_many :tenant_groups,
inverse_of: :tenant,
dependent: :destroy,
class_name: 'Tenant::Group'
validates :name,
presence: true,
length: { maximum: 255 }
end
class Tenant::Group < ApplicationRecord
has_rich_text :description
belongs_to :tenant,
class_name: 'Admin::Tenant',
inverse_of: :tenant_groups
validates :name,
presence: true,
length: { maximum: 255 }
acts_as_tenant :tenant
end
I also have the two factories defined:
FactoryBot.define do
factory :admin_tenant, class: 'Admin::Tenant' do
name { Faker::Lorem.sentence }
end
factory :tenant_group, class: 'Tenant::Group' do
association :tenant, factory: :admin_tenant
name { Faker::Lorem.sentence }
end
end
When utilizing the factory :admin_tenant
on its own, it seems to work fine, but when I attempt to generate a :tenant_group
(using create(:tenant_group)
) I receive an error in rspec:
Failure/Error: let(:tenant_group) { create(:tenant_group) }
ActiveRecord::AssociationTypeMismatch:
Tenant(#52813860) expected, got #<Admin::Tenant id: 295, name: "Perspiciatis sit numquam fugit.", created_at: "2020-01-03 15:08:13", updated_at: "2020-01-03 15:08:13"> which is an instance of Admin::Tenant(#55283820)
It seems that for some reason, it is assuming the class of the factory should be something else. Since I specify class_name
in the association, I'd assume it would work (it does when I'm using the application itself). I saw that Spring might cause issues, so I followed the advice of the FactoryBot README and placed config.before(:suite) { FactoryBot.reload }
in my rails_helper.rb file.
Update 1
Now finding out that the problem lies with acts_as_tenant
. The stack trace was too short in RSpec output to realize what the issue was, but now it's showing up in regular usage, as well.
Update 2
I'm going to go ahead and mark this as solved. It doesn't appear to be an issue with FactoryBot as I initially thought, but rather an issue with my understanding of acts_as_tenant
. When the class name cannot be easily inferred by the association name, you must specify the :class_name
option. This became clear after browsing the source code for a bit. In retrospect, it seems obvious, since all associations seem to behave the same way...
英文:
Good morning,
I am working on a proof of concept Rails application after a long break from using Rails. I set up RSpec tests, as well as FactoryBot and Faker to generate test data. In my app, I have two models:
class Admin::Tenant < ApplicationRecord
has_rich_text :description
has_and_belongs_to_many :users,
association_foreign_key: :admin_user_id,
foreign_key: :admin_tenant_id
has_many :tenant_groups,
inverse_of: :tenant,
dependent: :destroy,
class_name: 'Tenant::Group'
validates :name,
presence: true,
length: { maximum: 255 }
end
class Tenant::Group < ApplicationRecord
has_rich_text :description
belongs_to :tenant,
class_name: 'Admin::Tenant',
inverse_of: :tenant_groups
validates :name,
presence: true,
length: { maximum: 255 }
acts_as_tenant :tenant
end
I also have the two factories defined:
FactoryBot.define do
factory :admin_tenant, class: 'Admin::Tenant' do
name { Faker::Lorem.sentence }
end
factory :tenant_group, class: 'Tenant::Group' do
association :tenant, factory: :admin_tenant
name { Faker::Lorem.sentence }
end
end
When utilizing the factory :admin_tenant
on its own, it seems to work fine, but when I attempt to generate a :tenant_group
(using create(:tenant_group)
) I receive an error in rspec:
Failure/Error: let(:tenant_group) { create(:tenant_group) }
ActiveRecord::AssociationTypeMismatch:
Tenant(#52813860) expected, got #<Admin::Tenant id: 295, name: "Perspiciatis sit numquam fugit.", created_at: "2020-01-03 15:08:13", updated_at: "2020-01-03 15:08:13"> which is an instance of Admin::Tenant(#55283820)
It seems that for some reason, it is assuming the class of the factory should be something else. Since I specify class_name
in the association, I'd assume it would work (it does when I'm using the application itself). I saw that Spring might cause issues, so I followed the advice of the FactoryBot README and placed config.before(:suite) { FactoryBot.reload }
in my rails_helper.rb file.
Update 1
Now finding out that the problem lies with acts_as_tenant
. The stack trace was too short in RSpec output to realize what the issue was, but now it's showing up in regular usage, as well.
Update 2
I'm going to go ahead and mark this as solved. It doesn't appear to be an issue with FactoryBot as I initially thought, but rather an issue with my understanding of acts_as_tenant
. When the class name cannot be easily inferred by the association name, you must specify the :class_name
option. This became clear after browsing the source code for a bit. In retrospect, it seems obvious, since all associations seem to behave the same way...
答案1
得分: 2
错误很可能是由ActsAsTenant
引起的,而不是FactoryBot
,后者正在执行正确的操作。
当您创建具有相同名称的多个关联时,后者会覆盖前者。而acts_as_tenant :tenant
就是这样做的,它会破坏您已经建立的关联。虽然文档不是很详细,但acts_as_tenant
宏大致接受与belongs_to
相同的选项。
class Tenant::Group < ApplicationRecord
has_rich_text :description
acts_as_tenant :tenant,
class_name: 'Admin::Tenant',
inverse_of: :tenant_groups
validates :name,
presence: true,
length: { maximum: 255 }
end
英文:
The error is most likely caused by ActsAsTenant
and not FactoryBot
which is doing the right thing.
When you create multiple associations with the same name the later overwrite the former. And acts_as_tenant :tenant
does just that and clobbers the association you already set up. Its not very well documented but the acts_as_tenant
macro takes roughly the same options as belongs_to
.
class Tenant::Group < ApplicationRecord
has_rich_text :description
acts_as_tenant :tenant,
class_name: 'Admin::Tenant',
inverse_of: :tenant_groups
validates :name,
presence: true,
length: { maximum: 255 }
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论