diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb index 2d66b3d5..06adb6b5 100644 --- a/lib/cucumber/core/test/result.rb +++ b/lib/cucumber/core/test/result.rb @@ -44,6 +44,14 @@ def describe_to(visitor, *args) def to_s "✓" end + + def with_appended_backtrace(step) + self + end + + def with_filtered_backtrace(filter) + self + end end class Failed @@ -72,6 +80,14 @@ def with_duration(new_duration) self.class.new(new_duration, exception) end + def with_appended_backtrace(step) + exception.backtrace << step.backtrace_line if step.respond_to?(:backtrace_line) + self + end + + def with_filtered_backtrace(filter) + self.class.new(duration, filter.new(exception.dup).exception) + end end # Base class for exceptions that can be raised in a step defintion causing @@ -92,6 +108,18 @@ def with_message(new_message) def with_duration(new_duration) self.class.new(message, new_duration, backtrace) end + + def with_appended_backtrace(step) + return self unless step.respond_to?(:backtrace_line) + set_backtrace([]) unless backtrace + backtrace << step.backtrace_line + self + end + + def with_filtered_backtrace(filter) + return self unless backtrace + filter.new(dup).exception + end end class Undefined < Raisable diff --git a/lib/cucumber/core/test/runner.rb b/lib/cucumber/core/test/runner.rb index cd617639..99435584 100644 --- a/lib/cucumber/core/test/runner.rb +++ b/lib/cucumber/core/test/runner.rb @@ -97,6 +97,8 @@ def initialize(step_result) def execute(test_step, monitor, &continue) result = test_step.execute(monitor.result, &continue) + result = result.with_message(%(Undefined step: "#{test_step.name}")) if result.undefined? + result = result.with_appended_backtrace(test_step.source.last) if test_step.respond_to?(:source) result.describe_to(monitor, result) end diff --git a/spec/cucumber/core/test/result_spec.rb b/spec/cucumber/core/test/result_spec.rb index 5add4fc9..f6432f72 100644 --- a/spec/cucumber/core/test/result_spec.rb +++ b/spec/cucumber/core/test/result_spec.rb @@ -30,6 +30,14 @@ module Cucumber::Core::Test expect { Result::Passed.new }.to raise_error(ArgumentError) end + it "does nothing when appending the backtrace" do + expect( result.with_appended_backtrace(double) ).to equal result + end + + it "does nothing when filtering the backtrace" do + expect( result.with_filtered_backtrace(double) ).to equal result + end + it { expect( result ).to be_passed } it { expect( result ).not_to be_failed } it { expect( result ).not_to be_undefined } @@ -58,6 +66,31 @@ module Cucumber::Core::Test expect { Result::Failed.new(duration) }.to raise_error(ArgumentError) end + it "does nothing if step has no backtrace line" do + result.exception.set_backtrace("exception backtrace") + step = "does not respond_to?(:backtrace_line)" + + expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace"]) + end + + it "appends the backtrace line of the step" do + result.exception.set_backtrace("exception backtrace") + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).exception.backtrace ).to eq(["exception backtrace", "step_line"]) + end + + it "apply filters to the exception" do + filter_class = double + filter = double + filtered_exception = double + expect( filter_class ).to receive(:new).with(result.exception).and_return(filter) + expect( filter ).to receive(:exception).and_return(filtered_exception) + + expect( result.with_filtered_backtrace(filter_class).exception ).to equal filtered_exception + end + it { expect( result ).not_to be_passed } it { expect( result ).to be_failed } it { expect( result ).not_to be_undefined } @@ -80,6 +113,54 @@ module Cucumber::Core::Test it { expect( result ).not_to be_skipped } end + describe Result::Raisable do + context "with or without backtrace" do + subject(:result) { Result::Raisable.new } + + it "does nothing if step has no backtrace line" do + step = "does not respond_to?(:backtrace_line)" + + expect( result.with_appended_backtrace(step).backtrace ).to eq(nil) + end + end + + context "without backtrace" do + subject(:result) { Result::Raisable.new } + + it "set the backtrace to the backtrace line of the step" do + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).backtrace ).to eq(["step_line"]) + end + + it "does nothing when filtering the backtrace" do + expect( result.with_filtered_backtrace(double) ).to equal result + end + end + + context "with backtrace" do + subject(:result) { Result::Raisable.new("message", 0, "backtrace") } + + it "appends the backtrace line of the step" do + step = double + expect( step ).to receive(:backtrace_line).and_return("step_line") + + expect( result.with_appended_backtrace(step).backtrace ).to eq(["backtrace", "step_line"]) + end + + it "apply filters to the backtrace" do + filter_class = double + filter = double + filtered_result = double + expect( filter_class ).to receive(:new).with(result.exception).and_return(filter) + expect( filter ).to receive(:exception).and_return(filtered_result) + + expect( result.with_filtered_backtrace(filter_class) ).to equal filtered_result + end + end + end + describe Result::Undefined do subject(:result) { Result::Undefined.new } diff --git a/spec/cucumber/core/test/runner_spec.rb b/spec/cucumber/core/test/runner_spec.rb index dae21d4b..8d718761 100644 --- a/spec/cucumber/core/test/runner_spec.rb +++ b/spec/cucumber/core/test/runner_spec.rb @@ -100,6 +100,24 @@ module Cucumber::Core::Test expect( report ).to receive(:after_test_case) do |test_case, result| expect( result ).to be_undefined end + allow( undefined.source.last ).to receive(:name) + test_case.describe_to runner + end + + it 'sets the message on the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.message ).to eq("Undefined step: \"step name\"") + end + expect( undefined.source.last ).to receive(:name).and_return("step name") + test_case.describe_to runner + end + + it 'appends the backtrace of the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace ).to eq(["step line"]) + end + expect( undefined.source.last ).to receive(:backtrace_line).and_return("step line") + allow( undefined.source.last ).to receive(:name) test_case.describe_to runner end end @@ -113,6 +131,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'appends the backtrace of the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace.last ).to eq("step line") + end + expect( pending.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context "a skipping step" do @@ -124,6 +150,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'appends the backtrace of the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.backtrace.last ).to eq("step line") + end + expect( skipping.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context 'that fail' do @@ -135,6 +169,14 @@ module Cucumber::Core::Test end test_case.describe_to runner end + + it 'appends the backtrace of the exception of the result' do + expect( report ).to receive(:after_test_case) do |test_case, result| + expect( result.exception.backtrace.last ).to eq("step line") + end + expect( failing.source.last ).to receive(:backtrace_line).and_return("step line") + test_case.describe_to runner + end end context 'where the first step fails' do