1. zena on rails2 will use RSpec

    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.

    verbosity

    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 ?

    scopes

    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.

    conclusion

    RSpec is really a nice testing framework that will solve most of the problems I had with test/unit:

    • stubs and mocks produce less brittle tests !
    • scoped contexts eases readability and maintainability
    • assertions are easier to read and understand
    • thanks to the close collaboration with rails team and rcov, we now have great coverage information:
    bad coverage

    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

    1. Saturday, January 31 2009 13:10 Thomas R. Koll

      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

    2. Tuesday, February 03 2009 10:47 Gaspard Bucher

      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.

    3. leave a comment