Last week, RSpec 1.1.4 was released; and given my optimistic disposition I immediately upgraded. Happily, the upgrade was almost seamless. Two interdependent issues kept this from being a flawless upgrade:

  1. A new deprecation warning on our helper specs (no one really likes to see deprecation warnings)
  2. A bug in the new way to write helper specs (a show stopper)

Lets see what happens when we run our spec1.

Wonderland$ spec spec/helpers/dog_helper_spec.rb 
Modules will no longer be automatically included in RSpec version 1.1.4.  Called from ./spec/helpers/dog_helper_spec.rb:8
.Modules will no longer be automatically included in RSpec version 1.1.4.  Called from ./spec/helpers/dog_helper_spec.rb:16
.
Finished in 0.393129 seconds
2 examples, 0 failures

Lets examine the helper spec that generated these warnings:

 1require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
 2
 3describe DogHelper do
 4
 5  describe "name_or_description()" do
 6    it "should return a description for a dog without a name" do
 7      dog = mock_model(Dog, :name => nil, :sex => 'male', :breed => 'Labrador', :color => 'black')
 8      name_or_description(dog).should == "(male Labrador, black)"
 9    end
10  end
11
12  describe "owner_link()" do
13    it "should return a link to the owner" do
14      owner = mock_model(User, :login => 'sally_smith', :to_param => 'sally_smith')
15      dog = mock_model(Dog, :owner => owner)
16      owner_link(dog).should =~ /users\/sally_smith/
17    end
18  end
19end

So, what does this deprecation warning tell us? We are being warned that calls to name_or_description and owner_link, which are called on the implicit self of the RSpec it blocks, are going to fail in the future when the helper module (DogHelper in this example) is no longer automatically included by RSpec.

Or put another way, calling helper methods like name_or_description directly, exactly as one might in a view, will no longer work.

The new way to call helper methods is through the helper attribute of RSpec, giving access to an ActionView::Base instance with the helper module included. Lets go ahead and make this change to our two helper calls:

 1require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
 2
 3describe DogHelper do
 4
 5  describe "name_or_description()" do
 6    it "should return a description for a dog without a name" do
 7      dog = mock_model(Dog, :name => nil, :sex => 'male', :breed => 'Labrador', :color => 'black')
 8      helper.name_or_description(dog).should == "(male Labrador, black)"
 9    end
10  end
11
12  describe "owner_link()" do
13    it "should return a link to the owner" do
14      owner = mock_model(User, :login => 'sally_smith', :to_param => 'sally_smith')
15      dog = mock_model(Dog, :owner => owner)
16      helper.owner_link(dog).should =~ /users\/sally_smith/
17    end
18  end
19end

Lets try this out!

Wonderland$ spec spec/helpers/dog_helper_spec.rb 
.F
1) NoMethodError in 'DogHelper owner_link() should return a link to the owner'
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for
(eval):17:in `user_path'
/Users/leshill/src/rails/rspec114/app/helpers/dog_helper.rb:9:in `owner_link'
./spec/helpers/dog_helper_spec.rb:16:
Finished in 0.398686 seconds
2 examples, 1 failure

Hmmm, we have gotten rid of the deprecation warnings, but we now have a new (and truly unexpected!) failure.

This is a bona-fide bug in 1.1.4, and might lead to our having to rollback to 1.1.3 if there was no easy workaround. Thankfully, there is a fix already available for 1.1.5 , and that fix can be easily adapted within our own specs.

Lets add this before block to our spec:

1before(:each) do
2  # Patch until 1.1.5
3  helper_controller = @controller
4  helper.instance_eval { @controller = helper_controller }
5end

And then try it out:

Wonderland$ spec spec/helpers/dog_helper_spec.rb 
..
Finished in 0.40663 seconds
2 examples, 0 failures

Much better!

1 I am using a mock spec to demonstrate the issue, the minimal Rails app which fits around this spec is left as an exercise for the reader.

blog comments powered by Disqus