Relevance is releasing a new Behaviour Driven Development (BDD) framework, even though the Ruby community already has great tools like RSpec, Shoulda, and Context. Let me tell you why.
When I joined Relevance in 2007, I led a conversion from test/unit to test/spec. It was an easy win - we could use "expected.should == actual" syntax, which was nice, and using strings instead of "test_foo_bar" methods is huge. We wrote spec-converter to ease the conversion, went from test/unit to test/spec, and everyone was happy.
Enter 2008: RSpec gains momentum and we see its API stabilize; meanwhile we continue making little tweaks and hacks to our fork of test/spec. I added focused spec support, which basically means you change your "it" to a "fit", and that spec will run in isolation in your suite. This was a major deal for staying in flow with large and/or slow suites - if you have 15 broken specs, focus on the simplest one and make it pass, pick the next failing spec, lather, rinse, repeat. Once you are used to having focused specs, it becomes hard to go without them.
Chad Humphries joined Relevance in mid-2008, and brought with him a passion for RSpec. We had some "differences of opinion" when it came to RSpec vs. test/spec. I wanted support for focused tests and a small codebase that lent itself to extension; Chad wanted things that "just worked" from RSpec and the community of tools and knowledge that exists for it. At the time, test/spec also had some quirks that RSpec had already solved, like before blocks not inherited for nested describes, and better Rails support.
So Chad went off into a desert around Thanksgiving of last year and came back with Micronaut written on stone tablets in about a week. It was lean, mean, API-compatible with RSpec, and well under 2000 lines of implementation code. It can also be extended very easily to support focused specs, or slow specs, or ignored specs, or any other kind of metadata you would ever want in your suite. Each example (an "it" block, aka a spec) and each behavior (a describe block) accepts metadata as an options hash, which you can use to do just about anything you want -- filter what runs at runtime, tweak which modules get included/extended, or change the before/after blocks that evaluate.
A short example:
gem 'spicycode-micronaut'
require 'micronaut'
Micronaut.configure do |config|
config.filter_run :focused => true
config.alias_example_to :fit, :focused => true
end
describe "Metadata support in Micronaut" do
it "this will definitely run", :focused => true do
self.running_example.metadata[:focused].should == true
end
it "this never runs" do
raise "shouldn't run while other specs are focused"
end
fit "this is also focused via the fit alias" do
true.should_not == false
end
it "this won't run, its pending", :pending => true do
end
it "this is also pending, with the no block style"
it "this spec takes *real* long and should only run when we want to take the hit of slow specs", :speed => "slow" do
sleep(10000)
2+2.should == 4
end
end
Micronaut's metadata system is the next logical step in testing tools. Focused specs in test/spec was a small, fairly ugly, pragmatic hack. Micronaut makes metadata a first class citizen. True metadata becomes really important when you get into more significant codebases. You want this sort of power at the smallest level of granularity in your suite -- the individual example - as well as at higher levels. In Micronaut, metadata at the higher level of a describe block collapse down to nested describes and examples within describes, with lower level metadata always "winning" in the merge.
Micronaut is designed to be easily hackable and extendable, while staying small enough that you can easily find the right extension points and modules you want to change. It has made my day-to-day dev experience more effective and more enjoyable, because I really want to be able to own a tool so fundamental to my daily work as my testing tool. Although Micronaut is still pretty young and the internals are still evolving, the external API should remain fairly stable. Micronaut has been in real-world, production use on at least three Relevance client applications, many of our open source tools, and on the RunCodeRun codebase.
It is important to point out that we really like RSpec and continue to use it and support it. The state of BDD tools in the Ruby world owes a lot to David Chelimsky and team for their work on RSpec, and we love seeing the RSpec codebase get leaner and meaner as components get extracted out and Ruby 1.9 support matures. We also think tools like Shoulda, Bacon, Context are all good, because choice is a "good thing" for the entire Ruby open source community.
If you deal with larger codebases, or struggle with staying in flow in autotest, or just want to try a lean and mean BDD library, give Micronaut a try. The quick and easy way to try it is via the GitHub gem:
gem install spicycode-micronaut --source http://gems.github.com
Then drop the above example into a file to start playing. You can also check out the codes on GitHub, file feature ideas and bugs on Lighthouse, verify the build passes on RunCodeRun, and above all let us know what you think!
Comments
Reminds the new “tags” feature of cucumber, where you can set tags for scenarios and then filter execution to run only one (or more) of the tags you want at the moment.
Here is it’s doc: http://wiki.github.com/aslakhellesoy/cucumber/tags
Chad gave me a whirlwind tour when I came to visit last month. I was pretty impressed, and definitely look forward to using it. In particular, I liked that metadata is first class as you mentioned, and the API for things like mocking and formatters are cleanly implemented (ever looked at how mocha, rr, or redgreen hook into test/unit? scary stuff.)
Oh, and I’d like to mention that jeweler supports micronaut out of the box, so you can make micronaut-tested gems pretty easily:
You can also use micronaut easily with cucumber. Just add this to your features/support/env.rb:
Jeweler can handle generating that too:
Looks like the new tagging feature for RSpec: https://rspec.lighthouseapp.com/projects/5645/tickets/682-conditional-exclusion-of-example-groups
Can you describe the workflow around focused examples? I’m missing the point assuming you have an editor/IDE that’s smart enough to run just the spec where the cursor is and RSpec’s ability to make an example pending.
After looking at Micronaut a little more the focussed example integration with autotest is simply awesome! This has been a pain point for me and RSpec on larger projects as well. Micronaut definitely has some cool ideas worked into it. Thanks for sharing.
@John Hume Not to speak for Rob or Chad.. but I think the big win for the focused examples is when used with Micronaut’s autotest plugin. It allows you to use autotest on just the examples/example groups that you are “focussing” on. This prevents you from running your entire slow suite in the autotest cycle. I took a look at Micronaut’s own examples and fiddled with the settings while autotest was running to get a feel for how it works. IMO, it is really geared to solve the problem of still being able to use autotest on larger/slower suites.