aarkerio
aarkerio

Reputation: 2364

foreign_key and Fabricator-gem?

I have these models in Rails 6.1.7:

class AdminUser < ApplicationRecord
   belongs_to :organization, foreign_key: 'type_id'
end

class OrganizationUser < AdminUser
end

and defined in the file spec/fabricators/admin_user_fabricator.rb :

Fabricator(:admin_user) do
  email { Forgery(:internet).email_address }
  first_name { Forgery(:name).first_name }
  last_name { Forgery(:name).last_name }
end

Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do
  organization
end

but when I try to fabricate the attributes in my Rspec test:

 Fabricate.attributes_for(:organization_user) 

I'm getting:

    {"email"=>"[email protected]", "first_name"=>"Carolyn", "last_name"=>"Gutierrez", "organization_id"=>6919} 
F

Failures:   
    ActiveModel::UnknownAttributeError:
   unknown attribute 'organization_id' for OrganizationUser.

So, the attribute "organization_id" shouldn't be there. Besides, the attributes "type"=>"OrganizationUser" and "type_id"=>6939 should be there.

I don't haven experience with this gem yet. I tried several changes, but no luck so far. I don't know what I'm doing wrong.

Upvotes: 1

Views: 108

Answers (2)

lmtaq
lmtaq

Reputation: 456

There seems to be some confusion between your database tables structure, your models and the way Fabrication gem works.

The type field

It looks like you're working with Single Table Inheritance, so it makes sense to expect your database table to contain the type column. And you can verify that by running OrganizationUser.column_names or AdminUser.column_names. If STI was generated correctly, you should see the type column listed there.

This column will be automatically set whenever you instantiate an object for the child class.

> OrganizationUser.new.type
=> "OrganizationUser"

And a similar result is expected if you build an OrganizationUser object with fabrication:

> organization_user = Fabricate.build(:organization_user)
> organization_user.type
=> "OrganizationUser"

However, when you call Fabricate.attributes_for, you're not instantiating a model object. attributes_for only loads what's present in the definitions for your fabrications. That's why you don't see the type field listed there. Your fabricators have no type field. If you want it to be returned, you must explicitly add type "OrganizationUser" to your organization_user fabricator.

The organization_id attribute and the type_id column

The organization_id attribute is returned when you call for Fabricate.attributes_for(:organization_user) because you specified the association relation in your fabricator. Similar to the topic above, note that attributes_for will return a list according to your fabricator definitions. If an association is passed, it will try to generate an ID based on it (regardless of which foreign key you defined in your model).

I don't see much about Organization in your question. I'm assuming you also have an organizations table and an Organization model and that you added a type_id column to the admin_users table. You might want to go through those steps if any of these assumptions are incorrect. I'd suggest not using the type_id name for that FK unless you have a strong reason for that choice. If you could stick to the conventional FK organization_id, that can make your code easier to follow.

Fabricator suggestion

Assuming you're keeping type_id as the FK, you could have something like the following:

Fabricator(:organization_user, from: :admin_user, class_name: :organization_user) do
  type "OrganizationUser"
  type_id { Fabricate(:organization).id }
end

And then you call it on your specs without the need for extra params:

attributes = Fabricate.attributes_for(:organization_user)

Upvotes: 2

aarkerio
aarkerio

Reputation: 2364

As Lidiane suggested, I fixed the issue with:

let(:orguser_attrs) { Fabricate.attributes_for(:organization_user, type: 'OrganizationUser', type_id: Fabricate(:organization).id).except(:organization_id) }

Yeah, ugly as heck, I know. Bad designed models.

Upvotes: 0

Related Questions