Have you ever forgotten to setup required fixtures in spec because of their quantity? Sometimes we have up to 10..20 tables to be filled before spec runs. In opposite, you may write many tiny contexts, so that you need short fixtures definitions in each of them. But the latter leads to very unmaintainable un-DRY code, especially when you add new entity to the project which touches many specs.
One may consider make all fixtures global, so that you don’t need to use fixtures method at all. But having 500 specify blocks and loading 30 tables per each isn’t that fast.
What we need is auto-loaded fixtures. Once model Person is mentioned in specify block, fixture people is loaded. That strategy guarantees three things:
1. Models always get required data in database
2. We don’t spend time on loading non-used fixtures
3. We don’t have to manage one more dependency, which is not that cool at all.
The main difficulty here is how catch the moment when model class appers in specify do .. end. Each constant gets catched and autoloaded by active_support/dependencies, but it is done only once, so it is not the right place to fixturize :) database.
One of the possible solutions is to hide ActiveRecord model constants before specify so to catch them in const_missing. Constants are collected within AR::inherited and loaded or revealed in const_missing.
The main problem now is to figure out which evaluation context asks for constant. This is needed for calling fixtures in the right place. You may ask: why don’t you just add const_missing to every context object to catch constant exactly where fixture is to be loaded. I tried that. RSpec uses blocks for specs, and ruby blocks, as everybody knows, are pure closures. Constant mentioned in block context have no way to be searched for in block caller context. Even using instance_eval or eval with binding (that’s nearly the same as instance_eval in that case).
Another problem is to catch fixture shortcuts: like people(:oleganza). Person class may not be even loaded before such call, so we need to do some simple heuristics to load class and fixtures altogether.
Of course, instantiated fixtures (@people) are not supported. They are slow and nobody uses them anyway.
I’m still in search for working solution. Hope it will be nice. Stay tuned, more to come.