This week, Chris is joined by thoughtbot's Development Director in Boston, Josh
Clayton, to talk about factory_girl; a topic near and dear to Josh's
heart, as he's been the maintainer of the project for over five years.
Factory Girl is one of thoughtbot's most popular open-source projects, and is
one of the few gems that we consider a requirement for every project, new or
old. Let's discover why.
Do you guys recommend any sort of file structure for factories? Or do you toss everything in spec/factories/factories.rb?
I also have a question on creating associations with call backsâŚ(and maybe this one is outside the scope of comments on Upcase)⌠Letâs say I have a user that has_one :account. When creating the user and account association, do you prefer the after(:stub) callback? of the after(:build) callback?
FactoryGirl.define do
factory :user do
email "d@gmail.com"
factory :user_with_account do
after(:build) do |user|
FactoryGirl.build(:account, user: user)
end
# OR
after(:stub) do |user|
FactoryGirl.build(:account, user: user)
end
end
end
end
I know youâre a big fan of build_stubbed which would use the after(:stub) callback. Thoughts?
@thedanotto the current best practice recommended at thoughtbot is to put all your factories into spec/factories.rb. Thereâs some other nice guidelines there for how to organize attributes, traits, associations, etc. in your factories.
Iâve worked on projects where the factories were broken up by model class, which is fine as well (I personally prefer separate classes, but if you have any STI or subclassing the âall factories in one fileâ approach is certainly easier to search).
I think I would only create associations in an after(:create) callback. Iâm wary of checking for associations in a situation where the model under test hasnât been saved to the database. If youâre limiting your use of FactoryGirl.create to scenarios that require the database, then that use will be a signal that your test is database dependent even on a quick reading.
@christoomey
Thanks for the video, I learned a lot!
My $0.02 of feedback: Iâd have appreciated the video more if Josh as the maintainer would have had more opportunity to talk and if the video would have dived more into the nuances of FG. More code examples would have been nice as well.
Also, showing the code examples a bit longer before switching back would also work in your favor imho. The audio would sometimes be just fine while spending more time with the code examplesâthat way I wouldnât need to pause the video for that most of the time
@christoomey thanks for the video- seeing how you can play around with it in the console was really valuable. Using the console and trying things out is what helped me get good enough at Factory Girl to start using it in real life.
@christoomey@joshclayton Great video guys; I have some thoughts related to the way we normally use factory girls in general.
You guys mention the mystery guest anti pattern and I agree that Rails fixtures can lead to this problem; but I feel factory_girl leads to another different issue and is the fact that we are mutating the state of the database to make assertion against it. If we are testing a sub system of our application shouldnât there be an entry point in the production code base that put the system in that state and then we assert on the side effects? Factory girl is meant to be use from the test but I argue that it should be use exclusively to test active record models and not to generate dynamic data to test other parts of the system that could lead to hide concepts from the domain because we have âanother wayâ of putting the system in a certain state.
Let me know what you think about this; thanks in advance,
@cored you bring up a valid point, and one I think every developer struggles with. By creating literally all data through the UI, assuming it is well-abstracted to helper methods/page objects to decouple tests, the problem then becomes test suite times.
For example, by testing ownership of todos within an application, youâd have to write a test signing in one person, have them create a todo, sign out, sign in as someone else, and ensure they canât see the todo for the other person. In contrast, you could create a todo (without being specific about the owner), sign into a new account, and assert the todo isnât present.
There are definitely cases where it can be difficult to model state in factory girl because of the complexity. In those situations, itâs difficult to provide a blanket answer, but if youâre diligent about factory maintenance, that often works. Alternatively, you could create a baseline with FG and drive parts of the internal state changing through the UI to help guarantee the correct state in a flow.
@joshuaclayton I see what you mean; but that would be a pain if we keep testing the entire stack. I think it will be easier to just call the service layer which to me in a system represent the entry point for an use case and put the system under test in that same context. The advantage of this is that if we change something it will fail for the right reasons at the test level. With factory girl since we are mutating the state of the data not exactly the state of the system as a whole it will create an illusion that the data is actually the application business logic instead of thinking about it as where we save things. Right now Iâm using factory girl when I do test exclusively for the persistence logic; I try to create concepts for every entry point in the system so I can use then without calling internal implementation details as for example the way we store first_name and password. I talked to much I hope itâs more clear the point I want to pass across.