While working on GoRunNow this weekend I ran headlong into the issue of foreign key constraints in Rails. In short, the creation of foreign key constraints is not supported by Rails out of the box. (There are plugins to add support, but I’m trying to keep my plugin usage to a minimum.)
Of course, you can execute raw SQL in your migrations via the execute method, which is what I initially did. The surprise came when I started writing specs and discovered the associated model could be saved without the foreign key. I was expecting the database constraint to prevent this.
After an hour of head scratching and digging I discovered the catch is your test database gets created from schema.rb, which is generated via inflection on the database schema after migrations are applied. It’s not based on the migrations themselves, so those foreign key constraints created via execute are sneakily left out of schema.rb.
From what I’ve read the reason for doing this is speeding up tests, which makes sense, but it was rather surprising behavior for someone new to BDD in Rails.
Since starting my new pet project, I’ve been experimenting with a more BDD style method of development. In particular, I’ve been following the typical BDD development loop:
- Write the spec for a small piece of code
- See spec fail (since the code doesn’t exist yet)
- Write the code to pass the spec
- See spec pass
- Repeat…
I won’t go into details on the “why” for doing development like this. There’s a lot of information, arguments, rebuttals, etc. out there. I’m just going to highlight some of the key benefits I’ve seen thus far.
Ruby on Rails
This is probably obvious to anyone who’s taken even a cursory glance at Rails, but it’s worth mentioning. Ruby on Rails as a framework and as a developer community has excellent support and attention to both test and behavior driven development. Test driven development is supported right out of the box, and behavior driven development is easily integrated via Rspec and Rspec for Rails. It’s worth mentioning because once you go back to a framework (or language for that matter) that doesn’t help you practice TDD or BDD (I’m looking at you Java/J2EE), it’s a big deal. Yeah, I know you can (and should) practice TDD/BDD in Java/J2EE, but it doesn’t have that momentum.
RSpec and RSpec for Rails
RSpec was the first framework for BDD in Ruby, so there’s a wealth of information out there for using it. There are other frameworks and some amount of noise in the community about what’s “best”, but I’ve been happy using RSpec and RSpec for Rails thus far.
ZenTest / autotest
The autotest gem (part of the larger ZenTest suite) watches your source tree and runs the relevant tests/specs for you automatically as you update individual files. If a test/spec fails, the command line output goes red. If it passes, it goes green. Simple, but very effective.
For me, autotest has been the key to making this style of development work. Next to my emacs window, I have a terminal running autotest in a tiny font (so it doesn’t take up a lot of screen real estate). When I write a new spec and save the file, I see the failure in a few seconds. When I write the code to pass the spec, I see it pass in just a few seconds as well. Thus it makes this whole development cycle much faster since steps 2 & 4 above are completely automated and I see the results of 1 & 3 almost instantly.
After doing development like this for a few weeks now, it pains me to go back to the old lengthy write/compile/test(manually) loop. Using this BDD method of development, I find I’m much more focused on all the details of my code… structure, brevity, quality, organization… rather than just getting something that works. Some of it is related to the benefits of behavior driven design and development, but a lot of it is that I can stay engaged (yeah, I’m talking about flow). No waiting for compile loops or opening browsers or bouncing around checking logs, etc. I’m able to write and test/spec a large amount of code w/out ever taking my hands off the keyboard.
It has taken a little while to learn the ropes and all the tricks for spec’ing code. It was frustrating at first because I was spending a lot of time writing specs and very little time writing my application, but now that I’m becoming more familiar with how to spec my code that has dropped considerably. Like a lot of expert power tools with a high learning curve (like vim and emacs), the payoff is really high once you get the hang of it.