Time passes and now, RSpec is much more useable then it used to be a year ago. Especially, it’s not a new syntax for unit tests anymore. It has become a new testing (and specifying) framework with many enhancements over test/unit.
The following example illustrates the huge progress made by RSpec:
describe Zena::Application::Visitor do describe "lang when logged in" do it "should equal the visitor's lang" do helper.stub!(:visitor).and_return(mock_model(User, :lang => 'en')) helper.lang.should == 'en' end end end
First, we do not rely on fixtures because we do not load the full stack ! Mocks and stubs let us test our helper in isolation. This will make testing really less brittle.
It also clarifies what we are testing: “full application” or some specific class
or module
. This has always been pretty clear when testing models, but we had to hack around to test helpers properly.
What about our the griefs we had with using pseudo english for testing ? Well the “pseudo english” parts can be much avoided and you can consider these parts as comments that happen to show in your tests (which is nice).
Some of the strange aspects of using “should” are in fact better alternatives to “assert” because it’s very clear what is the target value and what is the tested value:
assert_equal 'en', lang # or maybe assert_equal lang, 'en' ? # versus lang.should == 'en'
And finally, we can group related tests with scope “describe …. do” and if we keep it simple, it can be very readable and understandable. I think the use of expressions like it_should fullfill_role("account") do |contract|
should really be avoided because they confuse the reader: is it a comment ? is it an assertion ? what is going on behind it_should fullfill_role
?
This is the killer feature of RSpec ! You stub and mock just enough for a context and can reuse tests that should apply to more the one context. Here is the list of tests for zen_path, the thing that builds node urls:
describe Zena::Application::Url do describe "zen_path" do before do helper.stub!(:prefix).and_return('fr') helper.stub!(:current_site).and_return(mock_model(Site, :root_id => 123)) end describe "all nodes", :shared => true do it "should contain format if format is not 'html'" do helper.zen_path(@node, :format => 'xml').should =~ %r{\.xml$} end [...] end describe "on regular node" do before do @node = mock_model(Node, :basepath => nil, :custom_base => false, :klass => 'Post', :zip => 10) end it_should_behave_like "all nodes" it "should contain prefix, klass and zip" do helper.zen_path(@node).should == "/fr/post10.html" end end describe "on node with basepath" do # basepath = a parent has a custom_base before do @node = mock_model(Node, :basepath => 'docs/design', :custom_base => false, :klass => 'Post', :zip => 10) end it_should_behave_like "all nodes" it "should contain basepath before klass and zip" do helper.zen_path(@node).should == '/fr/docs/design/post10.html' end end [...] end end
See how the tests for “all nodes” are applied for “regular nodes”, “root node”, “node with basepath” ? See how @node
is specialized just enough but the other mocks are shared ? This is a very intelligent way to describe contexts and to exhaustively test a method.
RSpec is really a nice testing framework that will solve most of the problems I had with test/unit:

rake spec:rcov output show bad testing of the “sharp” option.
I might come back to edit this post when more parts are tested using RSpec. I am especially interested in testing zafu...
Gaspard Bucher
comments
A little warning as we at adva_cms are currently changing the way we test: Don’t stub too much. There will be a point where you will feel insecure if you test against your application or against the stubs.
Maybe you find some insights on our mailinglist:
http://groups.google.com/group/advabest-dev/browse_thread/thread/788e2f190d8483ce#
http://groups.google.com/group/advabest-dev/browse_thread/thread/2530ac649f4c40c0
Thanks for the warning. I will keep an eye on this. Unfortunately, I could not view the threads you mention since the group is not public.