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?
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.
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.
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.
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.
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 ...
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With