Recently I gave a talk at RubyKaigi 2014 about Rails fixtures. In this blog post I will be discussing some of the tips and tricks for using fixtures effectively.
You can also the see the video of this talk here.
Don’t use ids.. unless required
In fixtures, we can specify id of a fixture.
I would recommend not to specify the id. Rails generates the id automatically if we don’t explicitly specify it. Moreover there are a few more advantages of not specifying the id.
Stable ids for every fixture.
Rails will generate the id based on key name. It will ensure that the id is unique for every fixture. It can also generate ids for uuid primary keys.
Labeled references for associations like belongs_to, has_many.
Lets say we have a users table. And a user has many cars.
ferrari belongs to
john. So we have mentioned
user_id as 1.
When I’m looking at
cars.yml I see
user_id as 1. But now I to lookup to see which user has id as 1.
Here is another implementation.
Notice that I no longer specify
user_id for John. I have mentioned
name. And now I can reference that name in
cars.yml to mention that
How to set a value to nil from fixture
Let’s say that I have a boolean column which is
default. But for an edge case, I want it to be nil. I can obviously
mutate the data generated by fixture before testing. However I can
achieve this in fixtures also.
Specify null to make the value nil
As you can see above if the value is
null then YAML will treat it as
Leave the value blank to make the value nil
As you can see above if the value is blank then YAML will treat it as
When model name and table name does not match
Generally in Rails, the model name and table name follow a strict convention.
Post model will have table name
Using this convention, the fixture file for
Post models is obviously
But sometimes models do not match directly with the table name. This could be because of legacy reason or because of namespacing of models. In such cases automatic detection of fixture files becomes difficult.
set_fixture_class method for this purpose. This is a class method which accepts a hash where key should
be name of the fixture or relative path to fixture file and value should be model class.
I can use this method inside
test_helper.rb in any class inheriting from
values interpolation using $LABEL
Rails provides many ways to keep our fixtures DRY. Label interpolation is one of them. It allows the use of key of fixture as a value in the fixture. For example:
$LABEL is not a global variable here. Its just a placeholder.
$LABEL is replaced by the key of the fixture. And as discussed earlier the key of the fixture in this case is
So $LABLE has value
Before this PR, I could only use
this feature if the value is exactly $LABEL. So if the email is
email@example.com I could not use the
But after this PR, I can $LABEL anywhere in the string, and Rails
will replace it with the key.
So the earlier example becomes:
I use YAML defaults in database.yml for drying it up and keeping common configuration at one place.
I can use it for drying up fixtures too for extracting common part in our fixtures.
Note the usage of key
DEFAULTS for defining default fixture.
Rails will automatically ignore any fixture with key
If we use any other key then a record with that key will also get inserted in the database.
Database specific tricks
Fixtures bypass the normal Active Record object creation process. After reading them from YAML file, they are inserted into database directly using insert query. So they skip callbacks and validations check. This also has an interesting side-effect which can be used for drying up fixtures.
Suppose we have fixture with timestamp:
If I are using PostgreSQL, I can replace the
now is not a keyword here. It is just a string. The actual query
looks like this:
So the value for
last_active_at is still just
now when the query
The magic starts as PostgreSQL starts reading the values.
a shorthand for the current timestamp . As soon as Postgres reads it,
now with the current timestamp and the column
last_active_at gets populated with current timestamp.
I can also use the
now() function instead of just