Ruby – Rspec let scoping

rspecruby

I believe I have a problem with rspec let and scoping. I can use the methods defined with let in examples (the "it" blocks), but not outside (the describe block where I did the let).

5   describe Connection do
8     let(:connection) { described_class.new(connection_settings) }
9 
10    it_behaves_like "any connection", connection
24  end

When I try to run this spec, I get the error:

connection_spec.rb:10: undefined local
variable or method `connection' for
Class:0xae8e5b8 (NameError)

How can I pass the connection parameter to the it_behaves_like?

Best Answer

let() is supposed to be scoped to the example blocks and unusable elsewhere. You don't actually use let() as parameters. The reason it does not work with it_behaves_like as a parameter has to do with how let() gets defined. Each example group in Rspec defines a custom class. let() defines an instance method in that class. However, when you call it_behaves_like in that custom class, it is calling at the class level rather than from within an instance.

I've used let() like this:

shared_examples_for 'any connection' do
  it 'should have valid connection' do
    connection.valid?
  end
end

describe Connection do
  let(:connection) { Connection.new(settings) }
  let(:settings) { { :blah => :foo } }
  it_behaves_like 'any connection'
end

I've done something similar to bcobb's answer, though I rarely use shared_examples:

module SpecHelpers
  module Connection
    extend ActiveSupport::Concern

    included do
      let(:connection) { raise "You must override 'connection'" }
    end

    module ClassMethods
      def expects_valid_connection
        it "should be a valid connection" do
          connection.should be_valid
        end
      end
    end
  end
end

describe Connection do
  include SpecHelpers::Connection

  let(:connection) { Connection.new }

  expects_valid_connection
end

The definition of those shared examples are more verbose than using shared examples. I guess I find "it_behave_like" being more awkward than extending Rspec directly.

Obviously, you can add arguments to .expects_valid_connections

I wrote this to help a friend's rspec class: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html ...

Related Topic