Integral factories in tests with FactoryGirl
FactoryGirl may cause issues in tests when you have complicated relations in a database. If you don’t pay enough attention to integrity in your tests there is a probability to stuck with the inconsistent data. An idea of the blog post to show the problem and give a solution to avoid the situation in your work. Also the solution will prevent some Ruby developers from the issue.
Imagine you have a
User model and it is related to an
Account with “many to many” relation. Both
Account belong to a
Company and it’s impossible to attach a user from a company #1 to an account from company #2. In general this is the obvious business rule and usually developers don’t have a validation or a restriction for the rule in a persistence layer of an application. Mostly the rule is implemented in a business layer (this is a controller’s layer in MVC frameworks).
This is the UML diagram of the tables:
ActiveRecord we would specify the following classes and relations:
NOTE: We won’t discuss here why we don’t use the Rails’
has_many_and_belongs_toassociation. The topic worse its own discussion and its up to you what to use. But the specified relations will allow to understand the problem.
Then using FactoryGirl in the tests you will have the following obvious (at first glance) factories:
Now we are on the last step to realize the problem. Let’s go to rails console in test environment and try to create an account-user model with the specified
Look at the result of the second and third commands - they return company #1 and company #2 respectively and this is the issue. Remember that we have the business rule that the situation is not possible. In a layer above (may be in controllers, form objects, policy objects or elsewhere) we may have the validation and the application is ready to use in production or development mode. But when we run tests we have the inconsistency and this may cause a lot of problems in your tests starting from performance issues and ending with unexpected behavior, which is difficult to debug to identify the problem.
If you still don’t get the problem this is a clue which may show that you do things in a wrong way - in tests you can have the following stepped preparation of the environment:
And this combination gives us a valid relation account user finally. Note, to create the valid relation we have four lines of code instead of simple one:
create(:account_user). Imagine that you have a lot of such relations in a database and you should understand the nightmare.
FactoryGirl has much useful functionality and one of them solves the problem very easily. This is the
ignore method (from 29 April of 2014 it is renamed to
transient and as I understand the new release will make you to use the new name). The method allows us to define virtual attributes on a factory. After this we will be able to pass additional options constructing a model with the
build) method of
This is the simplified explanation a purpose of the method and it explains only my vision to the method. If you are not happy with the explanation or want to know more, please, read full documentation of this here.
Secondly we have to know that
FactoryGirl has lazy (in other words dynamic) attributes syntax. Pass a block to an attribute when you declare a factory and it will be evaluated on constructing a model each time and set the result to the attribute. The next code snippet will show syntax of the idea and you will understand what’s just explained.
Here we are and this is the improved factory:
Now test the factory:
Bingo! We’ve solved the issue with the few lines of code. Now our factories give us possibility to refactor the tests, they don’t create trash in the environment and the data is integral!
As a bonus we can even pass a custom company to the factory constructor and it will create for us a user in the company, an account in the company and the account user relation:
Awesome, isn’t it?
I know that somebody will say that these are obvious things but I’m sure that many Rails developers still have such issues in their projects. That’s why I decided to write the article to warn them.
FactoryGirl is powerful software which gives us cool features to use in web development using
Ruby language. But you should use it with a caution to identify issues, like you saw in this post and solve them in time. It will prevent you from a nightmare and, may be, will make you a happy Ruby developer. I don’t promise that you will be a happy Ruby developer after this, but at least your developing process should bring you more satisfaction.
Now you are armed with a tool which prevents you from the pitfalls which we have in our project. I wish you to not stuck into the issue.