Forum: RSpec Shared Helpers

Posted by Scott Taylor (Guest)
on 2010-02-23 18:33
(Received via mailing list)
Has there been any development on shared helpers / it_should_behave_like 
feature in rspec?

I forget the reasons, but I remember a patch for something like this was 
rejected:

    it_should_behave_like "an_entry", :locals => { :entry => Entry.new }

OR:

    before do
      @entry = Entry.new
    end

    it_should_behave_like "an_entry", :locals => lambda {
      {
        :entry => @entry
      }
    }

Is there any code which now deals with the variable passing issue in 
rspec?

Best,

Scott
Posted by Matt Wynne (mattwynne)
on 2010-02-23 18:43
(Received via mailing list)
On 23 Feb 2010, at 17:30, Scott Taylor wrote:

> OR:
>
> Is there any code which now deals with the variable passing issue in  
> rspec?
>
> Best,
>
> Scott

FWIW, what I've done in this situation is expect that the ExampleGroup
I've mixed the shared behaviour into to define a method, and call that
from the shared examples - if it's not defined you'll find out pretty
quickly. Another approach is to just expect the @variable to be set:

   describe "an entry" do
     before
       @entry or raise("You need to set @entry to use this shared
example group")
     end
   end

It's not great, but it makes the shared example group a little more
self-documenting than if you just shared the instance variable without
the check.

cheers,
Matt

http://mattwynne.net
+447974 430184
Posted by Pat Maddox (Guest)
on 2010-02-23 19:41
(Received via mailing list)
I just use a factory method.

describe 'Authorize.net CIM gateway', :shared => true do
  describe 'saving a card' do
    describe 'preconditions' do
      it "should raise an error if the card is not saved" do
        lambda {
          gateway.save_credit_card(Factory.build(:credit_card, :user => 
Factory(:user)))
        }.should raise_error(ArgumentError, /unsaved record/)
      end
    end

    describe 'first card saved on an account' do
      it "should set the authorize_id on the credit card" do
        credit_card = Factory :credit_card, :user => Factory(:user)
        gateway.save_credit_card credit_card
        credit_card.authorize_id.should be_present
      end

      it "should return true for a successful response" do
        credit_card = Factory :credit_card, :user => Factory(:user)
        gateway.save_credit_card(credit_card).should be_true
      end

      it "should mark the card as having a validated card code" do
        card = Factory :credit_card, :card_code_validated => false
        gateway.save_credit_card card
        card.should be_card_code_validated
      end
    end

  # etc etc etc etc
  end
end


describe Payment::AuthorizeNetCim::FakeGateway do
  def gateway
    @gateway ||= Payment::AuthorizeNetCim::FakeGateway.new
  end

  it_should_behave_like 'Authorize.net CIM gateway'

  # some extra custom examples
end

I prefer this to requiring an instance variable, because you get built 
in error-handling for free.  If you don't implement the factory method, 
you get "undefined method: gateway" rather than the less obvious 
undefined method on NilClass error.

Also, your :locals => { :entry => Entry.new } syntax is going to be no 
good because you'll only have one instance of Entry that gets reused 
across examples.  No bueno.  You'd have to do
:locals => lambda { { :entry => Entry.new } }
and now it's just getting ugly.

If you think defining methods is too heavy (which I wouldn't agree with, 
but okay) then maybe use the let method?

describe Entry do
  before do
    let(:entry) { Entry.new )
  end

  it_should_behave_like "an_entry"
end

How's that look?

Pat
Posted by Nicolás Sanguinetti (Guest)
on 2010-02-23 20:15
(Received via mailing list)
On Tue, Feb 23, 2010 at 3:41 PM, Matt Wynne <matt@mattwynne.net> wrote:
>>   it_should_behave_like "an_entry", :locals => { :entry => Entry.new }
>>     }
> mixed the shared behaviour into to define a method, and call that from the
> It's not great, but it makes the shared example group a little more
> self-documenting than if you just shared the instance variable without the
> check.
>
> cheers,
> Matt

We do what Matt does, plus we document on top of the shared examples
the interface the example group needs:

# You need to provide an <invalid_record> method that returns a record 
without
# saving, in an invalid state, for this mixin to work.
shared_examples_for "a model that can disable validations" do
  context "when checking for a record's validity" do
    it "always returns true if you disabled validations" do
      described_class.disable_validations!
      invalid_record.should be_valid
    end

    it "falls back to the usual behavior if you enable validations" do
      described_class.enable_validations!
      invalid_record.should_not be_valid
    end
  end

  ...
end

describe User do
  let :invalid_record do
    User.make_unsaved(:invalid)
  end

  it_should_behave_like "a model that can disable validations"
end


Cheers,
-foca
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.