Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add support for passing parameters and a block through include_examples #503

Merged
merged 4 commits into from

3 participants

@cbascom

The it_should_behave_like and it_behaves_like methods of pulling in shared examples both allow you to pass parameters and a customization block. In my case I preferred the include_examples method instead of those alternatives but it did not support passing parameters or a block. This change adds that support along with tests for it.

@dchelimsky
Owner

I think this is a good idea, but we should do it for include_context as well, and see how to merge/integrate this with it_behaves_like (since they would now all do fundamentally the same thing). Wanna take a crack at that?

@cbascom

Sure, I will take a look at that and see what I can come up with.

@myronmarston

I didn't even know include_examples existed before now. I guess you learn something new every day :).

@dchelimsky: what are the semantics of include_examples supposed to be, compared to it_behaves_like and include_context? I understand the difference between it_behaves_like and include_context (one creates a nested group and includes the shared behavior; the other includes the shared behavior directly in the current example group).

If include_examples is meant to be an alias for it_behaves_like, then I think we can just have it use alias_it_should_behave_like_to to define it as an alias.

As for allowing customization via parameters and a block for include_context...I think we discussed this at one point when we introduced include_context and, for some reason, decided against it...but now I forget why :(.

@cbascom

I went ahead and added this support in the hopes that Myron is mis-remembering and there is no reason to not make this change :).

In the current implementation include_examples and include_context do basically the same thing (include the shared behavior in the current example group). The only difference is that include_context is intended to be used to only include a shared context with no examples, but there is no code enforcement of this.

@dchelimsky
Owner

@myronmarston - include_examples and include_context have the same semantics: they get evaluated in the current group, whereas it_should_behave_like and it_behaves_like generate nested groups. The only difference is the naming and the wording of the failure message when the shared content can't be found.

After a bit more thought, I think passing params to include_[context|examples] is OK, but passing a block is a bit odd since the block would get executed in the same context in which it is declared. i.e. the following are equivalent:

describe CheckingAccoung do
  include_examples "account" do
    let(:account) { CheckingAccount.new }
  end

  it "does something special" do
    account # refers to the account defined by let(:account)
  end
end

describe CheckingAccoung do
  let(:account) { CheckingAccount.new }
  include_examples "account"

  it "does something special" do
    account # refers to the account defined by let(:account)
  end
end

This strikes me as a recipe for confusion.

What do you think about structuring it such that include_[examples|context] supports params but not a block, while it_[behaves|should_behave]_like supports both params and a block.

@cbascom

I agree that adding the block doesn't make any sense, I removed that support in my latest commit.

I didn't add anything to raise an error if a block is provided, what do you think about that? The way it is now someone could provide a block thinking that include_[examples|context] work the same as it_[behaves|should_behave]_like and that block will just be silently ignored.

@dchelimsky
Owner

An error or a warning suggesting the user should use it_behaves_like if he/she wants that functionality would be good.

@cbascom

I added a warning message for both cases if a block is provided.

@dchelimsky
Owner

Great work. Thanks!

@dchelimsky dchelimsky merged commit 2273007 into from
@dchelimsky dchelimsky referenced this pull request from a commit
@dchelimsky dchelimsky changelog and cleanup for #503 96ef203
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
36 lib/rspec/core/example_group.rb
@@ -77,12 +77,8 @@ class << self
def self.define_nested_shared_group_method(new_name, report_label=nil)
module_eval(<<-END_RUBY, __FILE__, __LINE__)
def self.#{new_name}(name, *args, &customization_block)
- shared_block = find_shared("examples", name)
- raise "Could not find shared examples \#{name.inspect}" unless shared_block
-
group = describe("#{report_label || "it should behave like"} \#{name}") do
- module_eval_with_args(*args, &shared_block)
- module_eval(&customization_block) if customization_block
+ find_and_execute_shared_block("examples", name, *args, &customization_block)
end
group.metadata[:shared_group_name] = name
group
@@ -101,15 +97,37 @@ class << self
# Includes shared content declared with `name`.
#
# @see SharedExampleGroup
- def self.include_context(name)
- module_eval(&find_shared("context", name))
+ def self.include_context(name, *args)
+ if block_given?
+ block_not_supported("context")
+ return
+ end
+
+ find_and_execute_shared_block("context", name, *args)
end
# Includes shared content declared with `name`.
#
# @see SharedExampleGroup
- def self.include_examples(name)
- module_eval(&find_shared("examples", name))
+ def self.include_examples(name, *args)
+ if block_given?
+ block_not_supported("examples")
+ return
+ end
+
+ find_and_execute_shared_block("examples", name, *args)
+ end
+
+ def self.block_not_supported(label)
+ warn("Customization blocks not supported for include_#{label}. Use it_behaves_like instead.")
+ end
+
+ def self.find_and_execute_shared_block(label, name, *args, &customization_block)
+ shared_block = find_shared(label, name)
+ raise "Could not find shared #{label} #{name.inspect}" unless shared_block
+
+ module_eval_with_args(*args, &shared_block)
+ module_eval(&customization_block) if customization_block
end
def self.find_shared(label, name)
View
80 spec/rspec/core/example_group_spec.rb
@@ -919,6 +919,37 @@ def foo; 'foo'; end
end.to raise_error(ArgumentError,%q|Could not find shared context "shared stuff"|)
end
+ context "given some parameters" do
+ it "passes the parameters to the shared context" do
+ passed_params = {}
+
+ shared_context "named this with params" do |param1, param2|
+ it("has access to the given parameters") do
+ passed_params[:param1] = param1
+ passed_params[:param2] = param2
+ end
+ end
+
+ group = ExampleGroup.describe do
+ include_context "named this with params", :value1, :value2
+ end
+ group.run
+
+ passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
+ end
+ end
+
+ context "given a block" do
+ it "warns the user that blocks are not supported" do
+ group = ExampleGroup.describe do
+ self.should_receive(:warn).with(/blocks not supported for include_context/)
+ include_context "named this with block" do
+ true
+ end
+ end
+ group.run
+ end
+ end
end
describe "#include_examples" do
@@ -943,6 +974,55 @@ def foo; 'foo'; end
end
end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|)
end
+
+ context "given some parameters" do
+ it "passes the parameters to the named examples" do
+ passed_params = {}
+
+ shared_examples "named this with params" do |param1, param2|
+ it("has access to the given parameters") do
+ passed_params[:param1] = param1
+ passed_params[:param2] = param2
+ end
+ end
+
+ group = ExampleGroup.describe do
+ include_examples "named this with params", :value1, :value2
+ end
+ group.run
+
+ passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
+ end
+
+ it "adds shared instance methods to the group" do
+ shared_examples "named this with params" do |param1|
+ def foo; end
+ end
+ group = ExampleGroup.describe('fake group')
+ group.include_examples("named this with params", :a)
+ group.public_instance_methods.map{|m| m.to_s}.should include("foo")
+ end
+
+ it "evals the shared example group only once" do
+ eval_count = 0
+ shared_examples("named this with params") { |p| eval_count += 1 }
+ group = ExampleGroup.describe('fake group')
+ group.include_examples("named this with params", :a)
+ eval_count.should eq(1)
+ end
+ end
+
+ context "given a block" do
+ it "warns the user that blocks are not supported" do
+ group = ExampleGroup.describe do
+ self.should_receive(:warn).with(/blocks not supported for include_examples/)
+ include_examples "named this with block" do
+ true
+ end
+ end
+ group.run
+ end
+ end
end
describe "#it_should_behave_like" do
Something went wrong with that request. Please try again.