Ammeter: The Way to Write Specs for Your Rails Generators
Generators got a complete makeover with Rails 3 making them much easier to write but they’ve been very hard to test if you’re using RSpec. That’s changed now with the Ammeter Gem which lets you write RSpec specs for your generators.
Who writes generators?
Unless you’ve writing a gem you probably haven’t created a generator, but I bet you’re using one someone else created. If you’ve ever typed
- “rails g rspec:install” and a spec director appeared
- “rails g cucumber:install” and gotten a features directory
- “rails g model post title:string body:text” and gotten specs for your model
- “rails g model post title:string body:text” and gotten mongoid models insead active record ones
There are a number of resources for writing generators using thor including the generators guide or railscast #218 and I’m not going to go into that here. If you’re using TestUnit, like the rails core team, you can use Generators::TestCase which is part of Rails - Devise has some good examples. For those of us using RSpec we can now use Ammeter.
Writing Specs with Ammeter
First you need to tell your gem to use ammeter by 1) adding it to our bundle and 2) making it accessible to our specs.
Then we specify the behavior. We’ll look at an example using Mongoid’s
config generator and
its spec config_generator_spec.
The generator’s usage is rails generate mongoid:config [DATABASE_NAME] [options]
.
There’s some boilerplate setup you’ll need at the top of your spec:
- Since this spec file is in spec/generators it automatically uses ammeter
- Generators are not automatically loaded by Rails’ const_missing so we need to require it explicitly
- destination tells the generator where to put its output (we add enough “../”s to get us out of the spec directory)
- before { prepare_destination } clears the destination so each spec starts fresh (similar to active record rollback)
Now for the behavior:
- run_generator runs the generator (optionally letting you pass in arguments)
- file gives you access to a generated file
- it should exist makes sure the file was generated
- it should contain looks inside a generated file for a string or regex
When you’re generating migrations it is somewhat tricky because migration file names contain a timestamp. We need another example and will use acts_as_taggable-on’s migration generator We could write a spec for this as
You can see much of the same setup and then the new matcher
- it { should be_a_migration } which adds a timestamp to the filename
Why ‘Ammeter’?
An Ammeter is a measuring instrument used to measure the electric current in a circuit. Generators produce electricity and your specs measure your generators … cute huh :)
Feedback Welcome
Try Ammeter for your generators, I hope you find it useful. If it doesn’t meet your needs fork away - Ammeter is on github. I welcome any feedback, issues or pull requests.