One Line Tests Without the Smells


Jan 16, 2009

Everybody loves one line tests. That's one of the great things about shoulda. It allows you to test a lot of simple functionality using one liners, called "macros". Unfortunately, though, there are a lot of things about shoulda macros that make them less than ideal as a testing mechanism.

Firstly, they get called at class scope, so the variables from your setup blocks isn't available. A quick read through the archives of the shoulda mailing list proves that this confuses people. The workaround for this issue is a major code smell: string programming.

For example...

should_redirect_to 'post_url(@post)'

Here, 'post_url(@post)' is evaluated at the scope of the controller. A parse error in your string wouldn't point you to your should_redirect_to declaration. It would point you to a line inside of shoulda, where the instance_eval actually happens. Not good. There are lots of other reasons string programming sucks, too, but I won't get in to them here.

The second issue with macros is that failures often happen deep inside of shoulda. Backtraces often become completely useless, forcing you to open up shoulda and wade through it to figure out why your code is failing.

One More Problem

context "A blog post" do
  setup do
    @author = create_person
    @post   = create_post :author => @author
  end

  should "be editable by its author" do
    assert @post.editable_by?(@author)
  end
end

Don't see the problem?

Why bother writing self-documenting test code if you always have to explain it to the reader? Test names are essentially glorified comments and comments are frequently code smells. Furthermore, all the extra code required to create a test (should "" do ... end) almost certainly discourages one assertion per test. If the assertion is one line and the code can explain itself, why bother with all the other crap?

The Solution: Zebra

context "With a blog post" do
  setup do
    @author        = create_person
    @somebody_else = create_person
    @post          = create_post :author => @author
  end

  expect { @post.to be_editable_by(@author) }
  expect { @post.not_to be_editable_by(@author) }
end

But, what about the test name?

I'm glad you asked. This is where zebra gets really cool. The above code will create tests with the following names:

"test: With a blog post expect @post.to(be_editable_by(@author))"
"test: With a blog post expect @post.not_to(be_editable_by(@somebody_else))"

Now, that is self-documenting code.

The right tool for the job

The cool thing about zebra is that it's an extension to context or shoulda and matchy (shoulda support coming very soon). If you have a test that belongs in an it or should block, with a big, old-fashioned test name, you can have it. Just use should or it. When you have a short, self-documenting test, use expect. Best of both worlds.

Get It!

`sudo gem install giraffesoft-zebra`

http://github.com/giraffesoft/zebra