test/spec on Rails and assert_difference April 3rd
I have a certain feeling that the style of doing Behavior Driven Development (BDD) sees a little more adoption than, say, a year ago. With RSpec being the more rigorous kid on the block, test/spec by Christian Neukirchen seems to be a little less intruiging. (Yes, RSpec does have its place, too.)
What is test/spec after all?
To quote its website:
test/spec layers an RSpec-inspired interface on top of Test::Unit, so you can mix TDD and BDD (Behavior-Driven Development).
This especially convenient if you’re porting tests of an existing application over from TDD to BDD-style. test/spec blessed tests can be run alongside of Test::Unit based tests and they can even live in the same files if you dare. For a more thorough explanation of the reasoning behind BDD, see its Wikipedia entry.
test_spec_on_rails
Starting out as a simple endorsement by Rick Olson when he discovered test/spec, the test spec on rails plugin recently got a really nice make-over by Per Wigren.
This new version (which, technically, isn’t a version after all – we’re talking Subversion revisions here) provides all sorts of handy wrappers for more Rails-specific assertions, that previously had to be used in all their nakedness and purity before. Here’s a rundown of the new prettyness:
@user.should.validate
@user.should.not.validate
should.redirect_to :action => 'show'
status.should.be :success
template.should.be "foo/show.rhtml"
page.should.select "form#user_form"
Wrappers for assert_difference
If you’ve ever used assert_difference before (the original of which appeared as a code snippet on project.ion.ist), you’re probably unable to live without it. Not having to store before states manually to compare them to an after state in your tests is such a simple, yet effective way to DRY up your tests.
def test_new_publication
assert_difference(Publication, :count) do
post :create, :publication => {...}
# ...
end
end
But now that you’ve been drinking the test/spec kool-aid, what’s that assert_something call doing in your test? Well, what about this:
context 'Blog' do
specify 'should create blog' do
lambda {
Blog.create :title => '...'
}.should.change(Blog, :count)
end
end
Admittedly, the lambda { } part isn’t the sexiest on the planet. But it fits nicely into the rest of the “should” specifications. Oh, and there’s a “not” part, too!
Here’s the code that makes this work (assuming you’ve installed the test_spec gem1 and the test_spec_on_rails plugin2). Just put it at the bottom of test/test_helper.rb.
# Credits: http://project.ioni.st/post/218#post-218
module Test::Unit::AssertDifference
def assert_difference(object, method = nil, difference = 1)
initial_value = object.send(method)
yield
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
end
def assert_no_difference(object, method, &block)
assert_difference object, method, 0, &block
end
end
# Extension for test/spec/rails, wraps assert_difference
module Test::Spec::Rails
module ShouldDiffer
def differ(*args)
assert_difference(*args, &@object)
end
alias :different :differ
alias :change :differ
end
module ShouldNotDiffer
def differ(*args)
assert_no_difference(*args, &@object)
end
alias :different :differ
alias :change :differ
end
end
Test::Spec::Should.send(:include, Test::Unit::AssertDifference)
Test::Spec::Should.send(:include, Test::Spec::Rails::ShouldDiffer)
Test::Spec::ShouldNot.send(:include, Test::Unit::AssertDifference)
Test::Spec::ShouldNot.send(:include, Test::Spec::Rails::ShouldNotDiffer)
Have fun refactoring your tests!
-
sudo gem install test_spec -
script/plugin install http://svn.techno-weenie.net/projects/plugins/test_spec_on_rails/

1 comment
Jump to comment form