bacon cucumber sandwich
Dammit, scooped by Giles. I hint tantalizingly at an upcoming article on Bacon and Cucumber, and he goes and writes the damn thing. I was even gonna raid Flickr for an appropriate picture, just like he did (this one’s courtesy of user nicubunu):
Ah well. It turns out we have different things to say on the matter, anyway. Giles was making an important point about understanding the tools you work with, and you should listen to him. I’m just going to say something much more mundane about using these two particular test libraries together.
You’ll recall that Cucumber’s ancestor, the Story Runner, used to be part of RSpec. So it made sense to use RSpec as your one-stop testing shop, for unit tests, acceptance tests, mocks, stubs, and best of all, the beautiful “should” syntax for assertions.
Of course, RSpec has long supported drop-in replacements for most of these, should you have a favorite stub library or such. The spinning off of Cucumber has made it that much more timely to consider custom-fitting each layer of your tests.
You certainly can use RSpec’s assertions underneath your Cucumber stories, as we’ve seen. But let’s say I’m some kind of hypothetical curmudgeonly developer who doesn’t want to download an entire test + assertion + mock + stub framework, just to get nice assertions for Cucumber. What do I do?
Well, one option is Bacon (Christian Neukirchen’s nod to RSpec; “Spec” in German means “Bacon”), which gives you RSpec-style asssertions, but leaves you to find or invent your own mocking, Rails integration, Web bindings, and so on.
Because it comes without all those extras, and because it “cuts with the grain” of Ruby in its design, the Bacon core weighs in at a svelte 356 lines of code as of this writing—that’s counting comments, but not tests. If you stick to the basics, it feels a lot like RSpec, with two important distinctions worth mentioning here:
- RSpec assertions tend to use spaces and underscores to separate words, while Bacon
tends to use dots. So
answer.should be_truebecomesanswer.should.be.true. - RSpec always throws an exception when a test fails. Bacon
tailors its behavior to whether it thinks
you’re manually typing code into
irbor running an automated test. So to use Bacon with Cucumber, you have to throwBacon::Counter[:depth] = 1inside yourenv.rbsomewhere, effectively saying, “Hey, Bacon, we’re inside a test framework, even though you don’t see adescribeblock anywhere.”
So for the party-planning
example from
the book, all you’d need to do is require 'bacon', do
the counter-depth thing I described above, make a quick pass through
the shoulds to “decimalize” them, and… deal with
custom matchers.
For simplicity’s sake, Bacon omits some of RSpec’s fancier
matchers. In particular, object.should have_foo doesn’t
automatically call object.has_foo?. But we can get
pretty close to that with a custom matcher, which in Bacon is a
plain old top-level function:
def have(suffix, *args)
lambda do |obj|
obj.send "has_#{suffix}?", *args
end
end
Then, instead of party.should have_location, you’ll
write party.should.have(:location).
What have we gained from all this? If your project already has
code-level tests in RSpec, you can get should-style
assertions in your acceptance tests for free—might as well use
‘em. But if you don’t need the mocks and extras that come with RSpec
(maybe you’re testing non-Ruby project!), and you just want the
assertion syntax, that’s where Bacon comes in.
Anyway, have a look at the diff between an RSpec project and a Bacon one; it’s quite easy to migrate from one to the other. Me, I’m going to lunch. Enjoy!
