From 861e7d5537c7676c31f97d54d69e17b297a8e11d Mon Sep 17 00:00:00 2001 From: Xavier Shay Date: Sat, 25 Jan 2014 15:36:36 -0800 Subject: [PATCH] Pending blocks will now be executed and marked as failed if they succeed. This is a backwards incompatible change that makes the behaviour of a top-level `pending` call the sames as one used within an `it` block. The old "never run this example" behaviour is still available via the `xit` method or the `:skip` metadata option. Implements #1208. --- features/command_line/format_option.feature | 2 +- features/pending/pending_examples.feature | 18 ++++++++- lib/rspec/core/example.rb | 15 ++------ lib/rspec/core/example_group.rb | 37 ++++++++++++++++++- spec/rspec/core/example_group_spec.rb | 22 +++++++++-- .../documentation_formatter_spec.rb | 2 +- spec/rspec/core/pending_example_spec.rb | 14 ++++++- 7 files changed, 89 insertions(+), 21 deletions(-) diff --git a/features/command_line/format_option.feature b/features/command_line/format_option.feature index f7aa352931..016dc1649f 100644 --- a/features/command_line/format_option.feature +++ b/features/command_line/format_option.feature @@ -34,7 +34,7 @@ Feature: --format option end it "does something that is pending", :pending => true do - expect(5).to be > 3 + expect(5).to be < 3 end end """ diff --git a/features/pending/pending_examples.feature b/features/pending/pending_examples.feature index 9e59137c93..bb4fc99f88 100644 --- a/features/pending/pending_examples.feature +++ b/features/pending/pending_examples.feature @@ -77,6 +77,22 @@ Feature: pending examples And the output should contain "Expected pending 'something else getting finished' to fail. No Error was raised." And the output should contain "pending_with_passing_block_spec.rb:3" + Scenario: pending any arbitrary reason, with a top-level block that passes + Given a file named "pending_with_passing_block_spec.rb" with: + """ruby + describe "an example" do + pending("something else getting finished") do + expect(1).to eq(1) + end + end + """ + When I run `rspec pending_with_passing_block_spec.rb` + Then the exit status should not be 0 + And the output should contain "1 example, 1 failure" + And the output should contain "FIXED" + And the output should contain "Expected pending 'something else getting finished' to fail. No Error was raised." + And the output should contain "pending_with_passing_block_spec.rb:2" + Scenario: temporarily pending by prefixing `it`, `specify`, or `example` with an x Given a file named "temporarily_pending_spec.rb" with: """ruby @@ -138,7 +154,7 @@ Feature: pending examples expect(3+4).to eq(7) end pending do - expect("string".reverse).to eq("gnirts") + fail end end """ diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index c3b0c3bbf4..08928d5e53 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -41,11 +41,7 @@ def self.delegate_to_metadata(*keys) keys.each { |key| define_method(key) { @metadata[key] } } end - delegate_to_metadata :full_description, :execution_result, :file_path, :location, :skip - - def pending - @metadata.fetch(:pending, skip) - end + delegate_to_metadata :full_description, :execution_result, :file_path, :location, :skip, :pending # Returns the string submitted to `example` or its aliases (e.g. # `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to @@ -98,11 +94,8 @@ def example_group @example_group_class end - def skipped? - pending || skip - end - - alias_method :pending?, :skipped? + alias_method :pending?, :pending + alias_method :skipped?, :skip # @api private # instance_evals the block passed to the constructor in the context of @@ -269,7 +262,7 @@ def finish(reporter) reporter.example_pending self true elsif skipped? - record_finished 'pending', :pending_message => String === pending ? pending : Pending::NO_REASON_GIVEN + record_finished 'pending', :pending_message => String === skip ? skip : Pending::NO_REASON_GIVEN reporter.example_pending self true else diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 8163d8e363..054a99ac63 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -49,6 +49,25 @@ def description # @private alias_method :describes, :described_class + # @private + def self.wrap_pending_block(pending, block) + reason = if String === pending + pending + else + RSpec::Core::Pending::NO_REASON_GIVEN + end + + lambda {|*args| + pending(reason) do + if block + instance_exec(&block) + else + fail + end + end + } + end + # @private # @macro [attach] define_example_method # @param [String] name @@ -56,12 +75,26 @@ def description # @param [Block] implementation # @yield [Example] the example object def self.define_example_method(name, extra_options={}) + this = self define_method(name) do |*all_args, &block| desc, *args = *all_args options = Metadata.build_hash_from(args) options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block options.update(extra_options) - examples << RSpec::Core::Example.new(self, desc, options, block) + + pending = if name == :pending + desc || options[:pending] + else + options[:pending] + end + + callback = if pending + this.wrap_pending_block(pending, block) + else + block + end + + examples << RSpec::Core::Example.new(self, desc, options, callback) examples.last end end @@ -102,7 +135,7 @@ def self.define_example_method(name, extra_options={}) # Shortcut to define an example with :pending => true # @see example - define_example_method :pending, :skip => true + define_example_method :pending, :pending => true # Shortcut to define an example with :pending => 'Temporarily disabled with xexample' # @see example define_example_method :xexample, :skip => 'Temporarily disabled with xexample' diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index 00056072ff..37606fa5e7 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -888,11 +888,11 @@ def define_and_run_group(define_outer_example = false) end end - %w[pending xit xspecify xexample].each do |method_name| + %w[pending].each do |method_name| describe "::#{method_name}" do before do @group = ExampleGroup.describe - @group.send(method_name, "is pending") { } + @group.send(method_name) { fail } end it "generates a pending example" do @@ -900,12 +900,26 @@ def define_and_run_group(define_outer_example = false) expect(@group.examples.first).to be_pending end - it "sets the pending message", :if => method_name == 'pending' do + it "sets the pending message" do @group.run expect(@group.examples.first.metadata[:execution_result][:pending_message]).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end + end + end + + %w[xit xspecify xexample].each do |method_name| + describe "::#{method_name}" do + before do + @group = ExampleGroup.describe + @group.send(method_name, "is pending") { } + end + + it "generates a skipped example" do + @group.run + expect(@group.examples.first).to be_skipped + end - it "sets the pending message", :unless => method_name == 'pending' do + it "sets the pending message" do @group.run expect(@group.examples.first.metadata[:execution_result][:pending_message]).to eq("Temporarily disabled with #{method_name}") end diff --git a/spec/rspec/core/formatters/documentation_formatter_spec.rb b/spec/rspec/core/formatters/documentation_formatter_spec.rb index f8dd9223d6..5baaf948c7 100644 --- a/spec/rspec/core/formatters/documentation_formatter_spec.rb +++ b/spec/rspec/core/formatters/documentation_formatter_spec.rb @@ -58,7 +58,7 @@ module RSpec::Core::Formatters group = RSpec::Core::ExampleGroup.describe(" root ") context1 = group.describe(" nested ") context1.example(" example 1 ") {} - context1.example(" example 2 ", :pending => true){} + context1.example(" example 2 ", :pending => true){ fail } context1.example(" example 3 ") { fail } group.run(reporter) diff --git a/spec/rspec/core/pending_example_spec.rb b/spec/rspec/core/pending_example_spec.rb index 5db564e492..7f1404f3d4 100644 --- a/spec/rspec/core/pending_example_spec.rb +++ b/spec/rspec/core/pending_example_spec.rb @@ -11,10 +11,21 @@ end end + matcher :be_skipped_with do |message| + match do |example| + example.skipped? && example.metadata[:execution_result][:pending_message] == message + end + + failure_message_for_should do |example| + "expected: example skipped with #{message.inspect}\n got: #{example.metadata[:execution_result][:pending_message].inspect}" + end + end + context "declared pending with metadata" do it "uses the value assigned to :pending as the message" do group = RSpec::Core::ExampleGroup.describe('group') do example "example", :pending => 'just because' do + fail end end example = group.examples.first @@ -25,6 +36,7 @@ it "sets the message to 'No reason given' if :pending => true" do group = RSpec::Core::ExampleGroup.describe('group') do example "example", :pending => true do + fail end end example = group.examples.first @@ -40,7 +52,7 @@ end example = group.examples.first example.run(group.new, double.as_null_object) - expect(example).to be_pending_with('Not yet implemented') + expect(example).to be_skipped_with('Not yet implemented') end end