From 25ba15d6b61e052ed2a2faa97ff7cdfe8fc676a2 Mon Sep 17 00:00:00 2001 From: Mike Dalton Date: Fri, 29 Aug 2014 10:17:21 -0400 Subject: [PATCH] Add better `inspect` output for ExampleGroup Changing from defining a singleton method to storing the inspect output on the ExampleGroup Add documentation for instance_variables and inspect Handling string element in instance_variables for ruby 1.8 Removing un-needed expct calls in spec Handling ExampleGroup without inspect output, handling ExampleGroup without a description, refactoring logic to ignore `@__inspect_output` when determining before context ivars Improve readability of no inspect output spec and does not copy inspect output from before context to examples spec --- lib/rspec/core/example.rb | 11 +++- lib/rspec/core/example_group.rb | 33 ++++++++-- spec/rspec/core/example_group_spec.rb | 92 +++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 0304da46f8..8de2abdafa 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -82,6 +82,15 @@ def description RSpec.configuration.format_docstrings_block.call(description) end + # Returns a description of the example that always includes the location. + def inspect_output + inspect_output = "\"#{description}\"" + unless metadata[:description].to_s.empty? + inspect_output << " (#{location})" + end + inspect_output + end + # @attr_reader # # Returns the first exception raised in the context of running this @@ -170,7 +179,7 @@ def run(example_group_instance, reporter) rescue Exception => e set_exception(e) ensure - @example_group_instance.instance_variables.each do |ivar| + ExampleGroup.instance_variables_for_example(@example_group_instance).each do |ivar| @example_group_instance.instance_variable_set(ivar, nil) end @example_group_instance = nil diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index f12fedfc58..dcab883c38 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -419,9 +419,7 @@ def self.before_context_ivars # @private def self.store_before_context_ivars(example_group_instance) - return if example_group_instance.instance_variables.empty? - - example_group_instance.instance_variables.each do |ivar| + instance_variables_for_example(example_group_instance).each do |ivar| before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar) end end @@ -459,7 +457,8 @@ def self.run(reporter) reporter.example_group_started(self) begin - run_before_context_hooks(new) + instance = new('before(:context) hook') + run_before_context_hooks(instance) result_for_this_group = run_examples(reporter) results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all? result_for_this_group && results_for_descendants @@ -469,7 +468,8 @@ def self.run(reporter) RSpec.world.wants_to_quit = true if fail_fast? for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } ensure - run_after_context_hooks(new) + instance = new('after(:context) hook') + run_after_context_hooks(instance) before_context_ivars.clear reporter.example_group_finished(self) end @@ -495,7 +495,7 @@ def self.ordering_strategy def self.run_examples(reporter) ordering_strategy.order(filtered_examples).map do |example| next if RSpec.world.wants_to_quit - instance = new + instance = new(example.inspect_output) set_ivars(instance, before_context_ivars) succeeded = example.run(instance, reporter) RSpec.world.wants_to_quit = true if fail_fast? && !succeeded @@ -570,6 +570,27 @@ def self.pending_metadata_and_block_for(options, block) return options, callback end + + if RUBY_VERSION.to_f < 1.9 + # @private + def self.instance_variables_for_example(group) + group.instance_variables - ['@__inspect_output'] + end + else + # @private + def self.instance_variables_for_example(group) + group.instance_variables - [:@__inspect_output] + end + end + + def initialize(inspect_output=nil) + @__inspect_output = inspect_output || '(no description provided)' + end + + # @private + def inspect + "#<#{self.class} #{@__inspect_output}>" + end end # @private diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 8b560499c1..81edc3a51e 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -1604,5 +1604,97 @@ def foo; end end }.to raise_error(/not allowed/) end + + describe 'inspect output' do + context 'when there is no inspect output provided' do + it "uses '(no description provided)' instead" do + expect(ExampleGroup.new.inspect).to eq('#') + end + end + + context 'when an example has a description' do + it 'includes description and location' do + an_example = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe 'SomeClass1' do + example 'an example' do + an_example = self + end + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(an_example.inspect).to eq("#") + end + end + + context 'when an example does not have a description' do + it 'includes fallback description' do + an_example = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe 'SomeClass2' do + example do + an_example = self + end + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(an_example.inspect).to eq("#") + end + end + + it 'handles before context hooks' do + a_before_hook = nil + + group = ExampleGroup.describe 'SomeClass3' do + before(:context) do + a_before_hook = self + end + + example {} + end + + group.run + expect(a_before_hook.inspect).to eq("#") + end + + it 'handles after context hooks' do + an_after_hook = nil + + group = ExampleGroup.describe 'SomeClass4' do + after(:context) do + an_after_hook = self + end + + example {} + end + + group.run + expect(an_after_hook.inspect).to eq("#") + end + + it "does not pollute an example's `inspect` output with the inspect ivar from `before(:context)`" do + inspect_output = nil + + line = __LINE__ + 2 + group = ExampleGroup.describe do + example do + inspect_output = inspect + end + + before(:context) {} + end + + group.run + + path = RSpec::Core::Metadata.relative_path(__FILE__) + expect(inspect_output).to end_with("\"example at #{path}:#{line}\">") + end + end end end