Don't know if this is the best place to report this issue/bug (?), please direct me to the correct project repository if it is not.
I don't know if this is intended or not, but every time I try to access a variable declared in a let scope as an argument to a shared example group, I get the error:
example_group.rb:724:in `method_missing': `my_var` is not available on an example group (e.g. a `describe` or `context` block). It is only available from within individual examples (e.g. `it` blocks) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). (RSpec::Core::ExampleGroup::WrongScopeError)
from /home/lpec/projects/gh-trending/spec/parsers/repos_parser_spec.rb:63:in `block (4 levels) in <top (required)>'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
from /home/lpec/projects/gh-trending/spec/parsers/repos_parser_spec.rb:59:in `block (3 levels) in <top (required)>'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
from /home/lpec/projects/gh-trending/spec/parsers/repos_parser_spec.rb:49:in `block (2 levels) in <top (required)>'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
from /home/lpec/projects/gh-trending/spec/parsers/repos_parser_spec.rb:7:in `block in <top (required)>'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `module_exec'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:385:in `subclass'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/example_group.rb:258:in `block in define_example_group_method'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/dsl.rb:43:in `block in expose_example_group_alias'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/dsl.rb:84:in `block (2 levels) in expose_example_group_alias_globally'
from /home/lpec/projects/gh-trending/spec/parsers/repos_parser_spec.rb:3:in `<top (required)>'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `load'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `block in load_spec_files'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `each'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
from /home/lpec/.rvm/gems/ruby-2.3.0/gems/rspec-core-3.5.4/exe/rspec:4:in `<main>'
Here's the test setup:
context 'with provided language and period' do
let(:my_var) { :ruby }
context 'some ctx' do
it_should_behave_like 'some shared example', my_var
end
end
I'm not sure if this is intended or not (I guess the let blocks are only evaluated inside it blocks?), but it's kind of a bummer to have to explicitly repeat the variable when calling a shared example group like this (I would have to call it_should_behave_like 'some shared example', 'ruby'.
If I change the let block, I'll have to change it as well in the argument when invoking the shared example group.
I'm not sure if this is a bug or not. I'd love to help out and fix it if this is identified as an actual issue, otherwise. is there anyway I can achieve what I want here? (Not repeating the variable value when invoking the shared example). Defining the variable outside of the let works, but then it defies the purpose of having a proper test setup with let blocks (I think it's more idiomatic to use the constructs that rspec provides us to build context data).
This is not a bug. let declarations are only intended to be accessible from _within_ individually examples, not from within describe or context blocks (which are eagerly evaluated, and are the context in which to _define_ let declarations, hooks, examples, etc). If we allowed let declarations to be accessed outside examples it would have unfortunate consequences such as DB records being created outside of the per-example transactions, leading to inconsistent specs.
I don't understand why you are trying to pass my_var to it_should_behave_like when that let declaration is already accessible from within examples in the shared group. it_should_behave_like defines a nested example group, and just like normal nested example groups can access let declarations from parent groups, so can nested groups created via it_should_behave_like.
We might be able to give more specific advice if you could provide a complete concrete example of what you're trying to do.
You are right @myronmarston . I forgot the I could access the let variables inside the shared example group :)
but what if i want to send different "let" variables to the same shared examples block ?
what would be the best practice then ?
@LitalFiverr you can pass a black with let declarations to it_behaves_like:
it_behaves_like "special thing" do
let(:some_value) { 1 }
end
it_behaves_like "special thing" do
let(:some_value) { 2 }
end
@myronmarston
I don't understand why you are trying to pass
my_vartoit_should_behave_likewhen that let declaration is already accessible
The problem for me is that the use of these variables are not explicit. You cannot tell which variables are required when using shared examples. You either have to read through the code or do trial-and-error. It's the same with your block example.
The reason to use let instead of defining the arguments in place is e.g. that they can be used in other examples within the context.
Here's an example:
context "when there are multiple elements" do
let(:element_count) { 2 }
# This fails!
it_behaves_like "a normal collection", element_count
it "also behaves like a collection with multiple elements" do
expect(something).to eq(element_count)
end
end
Instead you're left with:
context "when there are multiple elements" do
let(:element_count) { 2 }
# I have no clue these use `element_count`
it_behaves_like "a normal collection"
it "also behaves like a collection with multiple elements" do
expect(something).to eq(element_count)
end
end
context "when there are multiple elements" do
let(:element_count) { 2 }
# I have to duplicate the variable
it_behaves_like "a normal collection", 2
it "also behaves like a collection with multiple elements" do
expect(something).to eq(element_count)
end
end
Alternatively I created a shared example to make the usage of variables explicit:
RSpec.shared_examples "requires variable" do |variable_name|
it "(shared example requires `#{variable_name}` to be set)" do
temp_config = RSpec::Expectations.configuration.on_potential_false_positives
RSpec::Expectations.configuration.on_potential_false_positives = :nothing
expect { send(variable_name) }.to_not raise_error(NameError)
RSpec::Expectations.configuration.on_potential_false_positives = temp_config
end
end
# Use it like:
RSpec.shared_examples "a normal collection" do
include_examples "requires variable", :element_count
# ...
end
See also on StackOverflow: https://stackoverflow.com/questions/48588739/rspec-how-to-pass-a-let-variable-as-a-parameter-to-shared-examples/50139613#50139613
The problem for me is that the use of these variables are not explicit. You cannot tell which variables are required when using shared examples. You either have to read through the code or do trial-and-error. It's the same with your block example.
That is a different problem and one that is exposed in overly complex shared example setups. Your solution is sensible and if you wanted to attempt a PR to allow specifying in a shared example that certain lets where available (and failing the entire group with an error if they are not) I'd be happy to help review it.
@JonRowe Thanks. Can't say that I will do that at this time but I'll keep it mind.
Most helpful comment
@LitalFiverr you can pass a black with
letdeclarations toit_behaves_like: