Reputation: 433
Update (Don't answer this)
I just learned that this question doesn't actually make sense. It was based on my own misconception of Factories and how they work.
The whole idea was based on a misconception of how FactoryBot works, in particular for some reason I was thinking that FactoryBot was setting a few variables that a completely different gem (Devise) was actually responsible for.
Is there any easy way to access the 'virtual attributes' for an already built factory?
Something like :attributes_for, but used on an instance of a Factory instead of the class?
So you could do something like this:
FactoryBot.define do
factory :user do
email { Faker::Internet.email }
password { "password" }
password_confirmation { "password" }
end
end
@user = FactoryBot.build(:user)
@user.factory_attributes # Not a real method
#-> { email: "[email protected]", password: "123456", password_confirmation: "123456" }
Why I Want This
If you're wondering, I want this to be able to shorten the following code for a 'Login' request spec.
From this:
let(:user_attributes) do
FactoryBot.attributes_for(:user)
end
let(:user) do
FactoryBot.create(:user, user_attributes)
end
# Triggers the create method in let(:user)
# Necessary to ensure the user exists in the database before testing sign in.
before { user }
let(:user_params) do
{ user: user_attributes }
end
it "redirects to the root path on successful sign in" do
post user_session_path(params: user_params)
expect(response).to redirect_to(root_path)
end
To this:
let(:user) do
FactoryBot.create(:user)
end
let(:user_params) do
{ user: user.factory_attributes }
end
it "redirects to the root path on successful sign in" do
post user_session_path(params: user_params)
expect(response).to redirect_to(root_path)
end
Which is significantly cleaner and less confusing than the first, especially for newer devs (could see someone with little RSpec experience spending quite some time trying to figure out just what in the hell the line "before { user }" is doing)
Upvotes: 1
Views: 1642
Reputation: 106972
FactoryBot.build(:user)
returns an instance of an ActiveRecord
model. Therefore you can just use ActiveRecord::Base#attributes
to return the list of attributes of the current object:
@user = FactoryBot.build(:user)
@user.attributes
Once the factory returned an instance of User
that user
has no information anymore about how it was initialized. Therefore it is impossible to read values that do not exist on the instance.
A workaround might be something like this:
let(:parameters) do
{ user: FactoryBot.attributes_for(:user) }
end
before do
FactoryBot.create(:user, parameters[:user])
end
it "redirects to the root path on successful sign in" do
post user_session_path(params: parameters)
expect(response).to redirect_to(root_path)
end
But actually, I think you should be more explicit about what attributes you really care about. You care about the user's email and user's password - all other attributes are not relevant in this spec. Therefore I would write the spec like this:
let(:email) { '[email protected]' }
let(:password) { 'secret' }
before do
FactoryBot.create(:user, email: email, password: password, password_confirmation: password)
end
it "redirects to the root path on successful sign in" do
post user_session_path(params: { user: { email: email, password: password } })
expect(response).to redirect_to(root_path)
end
Upvotes: 2
Reputation: 230461
Is there any easy way to access the 'virtual attributes' for an already built factory?
I think you are confused about terminology and/or how factory bot works. You don't build factories. Factory already exists and it builds users (in this case).
After a user is built/created, it has no knowledge of what factory built it. And rightly so. Users can be created in many ways. If that method did actually exist, what would you expect it to return when you create a user with User.create
?
Upvotes: 1