Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec let scoping

Tags:

ruby

rspec

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?

like image 958
Costi Avatar asked Apr 05 '11 15:04

Costi


People also ask

What does let do in RSpec?

The let method should be called inside an example group. The first argument is the name of a variable to define. The let method is passed a block that computes the value of the variable, and the block will be called if the value of the variable is ever needed. In other words, let variables are lazily evaluated.

What is describe in RSpec?

The word describe is an RSpec keyword. It is used to define an “Example Group”. You can think of an “Example Group” as a collection of tests. The describe keyword can take a class name and/or string argument.

What is context in RSpec?

According to the rspec source code, “context” is just a alias method of “describe”, meaning that there is no functional difference between these two methods. However, there is a contextual difference that'll help to make your tests more understandable by using both of them.

How do I download RSpec?

Installing RSpecBoot up your terminal and punch in gem install rspec to install RSpec. Once that's done, you can verify your version of RSpec with rspec --version , which will output the current version of each of the packaged gems. Take a minute also to hit rspec --help and look through the various options available.


2 Answers

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 ...

like image 127
Ho-Sheng Hsiao Avatar answered Oct 09 '22 03:10

Ho-Sheng Hsiao


Redacted -- completely whiffed on my first solution. Ho-Sheng Hsiao gave a great explanation as to why.

You can give it_behaves_like a block like so:

describe Connection do   it_behaves_like "any connection" do     let(:connection) { described_class.new(connection_settings) }   end end 
like image 33
bcobb Avatar answered Oct 09 '22 04:10

bcobb