
First let's talk about just what the heck a fixture is. In real estate they are defined as follows.
Accessories fixed to structures or land in such a way that they can't be independently moved without damage to themselves, or the property housing them.
Doesn't sound like something you want in your software does it? But I've used fixtures plenty! They aren't that bad. They are just an easy way of setting up test data. The reason for this write up is that recently I have heard lots of negative things about fixtures and I wanted to figure out for myself if they really are evil.
Now I always liked fixtures because I like my tests to be short, clean and DRY. Fixtures are a very cool, built in way to do that, and they can really speed up your test writing, which is great because it lowers the barrier to writing more tests. So fixtures keep things DRY, but at what price?
Jay Fields has an interesting article that suggests tests need not be DRY. Jay argues that isolation of tests is more important than clean, dry tests. I call this the "Every test for itself" philosophy of testing. Here is a quote from that article.
It means that when writing tests you should think about them in isolation. They run in isolation and the chances are when they break (if you write robust tests) they will be fixed in isolation; therefore, abstracting anything that is 'common' actually hurts maintainability. With every test, pretend it's the first and only test you are writing in that class. If that were true, it would be hard to justify those setup or private helper methods.
This is a totally awesome observation and agrees with my own experiences. As I type this I am thinking of all the convenience methods I have put into test classes. Whoops. Well, my tests were ultra clean, short and DRY tests for at least one day. The next day somebody who didn't notice my convenience method went ahead and re implemented it. Sigh.
Still, although I am going to try going without them for my next few projects, I think fixtures are convenient and when used properly there is nothing evil about them. So how do you use fixtures properly? Well perhaps it would be simpler to describe how to use them improperly. Here is a list from
Tom Preston-Werner's RailsConf 07 talk on what you don't want from your fixtures.
1. swampy (shows photo of swamp with trees with bad smells with stink-lines)
2. fixtures become an unmanageable mess
3. hard to keep track of links between data
4. things aren't easy to refactor
5. no namespacing – i.e. you need to have lots of users in different states for good testing
6. brittleness – one day you add a new column to a table, and half of your tests fail
7. no validation – there is no automatic way for ActiveRecord validation for fixture data
8. contamination – tests pass independently, but fail when running all at once
9. performance – fixtures can be slow
In practice all these, except maybe number 3 can be avoided. Gripes 4 and 6 are easily fixed by the
db:fixtures:migrate task. 2 and 8 are just a matter of being disciplined about writing, updating and using your fixtures. I'm not sure what swampy means, but it sure sounds bad. 9 can definately be a problem, especially if you are not using transactional fixtures (and if you aren't using a transactional database you aren't), but any test suite that uses the database can have that problem.
In the end it comes down to what you are comfortable with. Decide for yourself. If you can't live with Jay's "every test for itself" philosophy, then maybe fixtures ARE for you. Just keep clear of the 9 pitfalls Tom mentions and you will be fine. Remember it matters how you test, but the most important thing is that you test. Quoth
Testivus The perfect is the enemy of the good.
Don’t wait for best to do better.
Don’t wait for better to do good.
Write the test you can today.
The problems of maintaining a large suite of unit tests is considerable. However, the benefits of a test suite, even a troubled one, far outweighs the costs.
References:
- http://manuals.rubyonrails.com/read/chapter/26
- http://clarkware.com/cgi/blosxom/2005/10/24
- http://api.rubyonrails.org/classes/Fixtures.html