finnlabs-ci_reporter 1.6.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,189 @@
1
+ # Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'ci/reporter/core'
6
+
7
+ module CI
8
+ module Reporter
9
+ module RSpecFormatters
10
+ begin
11
+ require 'rspec/core/formatters/base_formatter'
12
+ require 'rspec/core/formatters/progress_formatter'
13
+ require 'rspec/core/formatters/documentation_formatter'
14
+ BaseFormatter = ::RSpec::Core::Formatters::BaseFormatter
15
+ ProgressFormatter = ::RSpec::Core::Formatters::ProgressFormatter
16
+ DocFormatter = ::RSpec::Core::Formatters::DocumentationFormatter
17
+ rescue LoadError => first_error
18
+ begin
19
+ require 'spec/runner/formatter/progress_bar_formatter'
20
+ require 'spec/runner/formatter/specdoc_formatter'
21
+ BaseFormatter = ::Spec::Runner::Formatter::BaseFormatter
22
+ ProgressFormatter = ::Spec::Runner::Formatter::ProgressBarFormatter
23
+ DocFormatter = ::Spec::Runner::Formatter::SpecdocFormatter
24
+ rescue LoadError
25
+ raise first_error
26
+ end
27
+ end
28
+ end
29
+
30
+ # Wrapper around a <code>RSpec</code> error or failure to be used by the test suite to interpret results.
31
+ class RSpecFailure
32
+ attr_reader :exception
33
+ def initialize(failure)
34
+ @failure = failure
35
+ @exception = failure.exception
36
+ end
37
+
38
+ def failure?
39
+ @failure.expectation_not_met?
40
+ end
41
+
42
+ def error?
43
+ !failure?
44
+ end
45
+
46
+ def name() exception.class.name end
47
+ def message() exception.message end
48
+ def location() (exception.backtrace || ["No backtrace available"]).join("\n") end
49
+ end
50
+
51
+ class RSpec2Failure < RSpecFailure
52
+ def initialize(example)
53
+ @example = example
54
+ @exception = @example.execution_result[:exception] || @example.execution_result[:exception_encountered]
55
+ end
56
+
57
+ def failure?
58
+ exception.is_a?(::RSpec::Expectations::ExpectationNotMetError)
59
+ end
60
+ end
61
+
62
+ # Custom +RSpec+ formatter used to hook into the spec runs and capture results.
63
+ class RSpec < RSpecFormatters::BaseFormatter
64
+ attr_accessor :report_manager
65
+ attr_accessor :formatter
66
+ def initialize(*args)
67
+ super
68
+ @formatter ||= RSpecFormatters::ProgressFormatter.new(*args)
69
+ @report_manager = ReportManager.new("spec")
70
+ @suite = nil
71
+ end
72
+
73
+ def start(spec_count)
74
+ @formatter.start(spec_count)
75
+ end
76
+
77
+ # rspec 0.9
78
+ def add_behaviour(name)
79
+ @formatter.add_behaviour(name)
80
+ new_suite(name)
81
+ end
82
+
83
+ # Compatibility with rspec < 1.2.4
84
+ def add_example_group(example_group)
85
+ @formatter.add_example_group(example_group)
86
+ new_suite(description_for(example_group))
87
+ end
88
+
89
+ # rspec >= 1.2.4
90
+ def example_group_started(example_group)
91
+ @formatter.example_group_started(example_group)
92
+ new_suite(description_for(example_group))
93
+ end
94
+
95
+ def example_started(name_or_example)
96
+ @formatter.example_started(name_or_example)
97
+ spec = TestCase.new
98
+ @suite.testcases << spec
99
+ spec.start
100
+ end
101
+
102
+ def example_failed(name_or_example, *rest)
103
+ @formatter.example_failed(name_or_example, *rest)
104
+
105
+ # In case we fail in before(:all)
106
+ example_started(name_or_example) if @suite.testcases.empty?
107
+
108
+ if name_or_example.respond_to?(:execution_result) # RSpec 2
109
+ failure = RSpec2Failure.new(name_or_example)
110
+ else
111
+ failure = RSpecFailure.new(rest[1]) # example_failed(name, counter, failure) in RSpec 1
112
+ end
113
+
114
+ spec = @suite.testcases.last
115
+ spec.finish
116
+ spec.name = description_for(name_or_example)
117
+ spec.failures << failure
118
+ end
119
+
120
+ def example_passed(name_or_example)
121
+ @formatter.example_passed(name_or_example)
122
+ spec = @suite.testcases.last
123
+ spec.finish
124
+ spec.name = description_for(name_or_example)
125
+ end
126
+
127
+ def example_pending(*args)
128
+ @formatter.example_pending(*args)
129
+ name = description_for(args[0])
130
+ spec = @suite.testcases.last
131
+ spec.finish
132
+ spec.name = "#{name} (PENDING)"
133
+ spec.skipped = true
134
+ end
135
+
136
+ def start_dump
137
+ @formatter.start_dump
138
+ end
139
+
140
+ def dump_failure(*args)
141
+ @formatter.dump_failure(*args)
142
+ end
143
+
144
+ def dump_summary(*args)
145
+ @formatter.dump_summary(*args)
146
+ write_report
147
+ end
148
+
149
+ def dump_pending
150
+ @formatter.dump_pending
151
+ end
152
+
153
+ def close
154
+ @formatter.close
155
+ end
156
+
157
+ private
158
+ def description_for(name_or_example)
159
+ if name_or_example.respond_to?(:full_description)
160
+ name_or_example.full_description
161
+ elsif name_or_example.respond_to?(:metadata)
162
+ name_or_example.metadata[:example_group][:full_description]
163
+ elsif name_or_example.respond_to?(:description)
164
+ name_or_example.description
165
+ else
166
+ "UNKNOWN"
167
+ end
168
+ end
169
+
170
+ def write_report
171
+ @suite.finish
172
+ @report_manager.write_report(@suite)
173
+ end
174
+
175
+ def new_suite(name)
176
+ write_report if @suite
177
+ @suite = TestSuite.new name
178
+ @suite.start
179
+ end
180
+ end
181
+
182
+ class RSpecDoc < RSpec
183
+ def initialize(*args)
184
+ @formatter = RSpecFormatters::DocFormatter.new(*args)
185
+ super
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,151 @@
1
+ # Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'delegate'
6
+ require 'stringio'
7
+
8
+ module CI
9
+ module Reporter
10
+ # Emulates/delegates IO to $stdout or $stderr in order to capture output to report in the XML file.
11
+ class OutputCapture < DelegateClass(IO)
12
+ # Start capturing IO, using the given block to assign self to the proper IO global.
13
+ def initialize(io, &assign)
14
+ super
15
+ @delegate_io = io
16
+ @captured_io = StringIO.new
17
+ @assign_block = assign
18
+ @assign_block.call self
19
+ end
20
+
21
+ # Finalize the capture and reset to the original IO object.
22
+ def finish
23
+ @assign_block.call @delegate_io
24
+ @captured_io.string
25
+ end
26
+
27
+ # setup tee methods
28
+ %w(<< print printf putc puts write).each do |m|
29
+ module_eval(<<-EOS, __FILE__, __LINE__)
30
+ def #{m}(*args, &block)
31
+ @delegate_io.send(:#{m}, *args, &block)
32
+ @captured_io.send(:#{m}, *args, &block)
33
+ end
34
+ EOS
35
+ end
36
+ end
37
+
38
+ # Basic structure representing the running of a test suite. Used to time tests and store results.
39
+ class TestSuite < Struct.new(:name, :tests, :time, :failures, :errors, :skipped, :assertions)
40
+ attr_accessor :testcases
41
+ attr_accessor :stdout, :stderr
42
+ def initialize(name)
43
+ super(name.to_s) # RSpec passes a "description" object instead of a string
44
+ @testcases = []
45
+ end
46
+
47
+ # Starts timing the test suite.
48
+ def start
49
+ @start = Time.now
50
+ unless ENV['CI_CAPTURE'] == "off"
51
+ @capture_out = OutputCapture.new($stdout) {|io| $stdout = io }
52
+ @capture_err = OutputCapture.new($stderr) {|io| $stderr = io }
53
+ end
54
+ end
55
+
56
+ # Finishes timing the test suite.
57
+ def finish
58
+ self.tests = testcases.size
59
+ self.time = Time.now - @start
60
+ self.failures = testcases.inject(0) {|sum,tc| sum += tc.failures.select{|f| f.failure? }.size }
61
+ self.errors = testcases.inject(0) {|sum,tc| sum += tc.failures.select{|f| f.error? }.size }
62
+ self.skipped = testcases.inject(0) {|sum,tc| sum += (tc.skipped? ? 1 : 0) }
63
+ self.stdout = @capture_out.finish if @capture_out
64
+ self.stderr = @capture_err.finish if @capture_err
65
+ end
66
+
67
+ # Creates the xml builder instance used to create the report xml document.
68
+ def create_builder
69
+ require 'builder'
70
+ # :escape_attrs is obsolete in a newer version, but should do no harm
71
+ Builder::XmlMarkup.new(:indent => 2, :escape_attrs => true)
72
+ end
73
+
74
+ # Creates an xml string containing the test suite results.
75
+ def to_xml
76
+ builder = create_builder
77
+ # more recent version of Builder doesn't need the escaping
78
+ def builder.trunc!(txt)
79
+ txt.sub(/\n.*/m, '...')
80
+ end
81
+ builder.instruct!
82
+ attrs = {}
83
+ each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty? }
84
+ builder.testsuite(attrs) do
85
+ @testcases.each do |tc|
86
+ tc.to_xml(builder)
87
+ end
88
+ builder.tag! "system-out" do
89
+ builder.text! self.stdout
90
+ end
91
+ builder.tag! "system-err" do
92
+ builder.text! self.stderr
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ # Structure used to represent an individual test case. Used to time the test and store the result.
99
+ class TestCase < Struct.new(:name, :time, :assertions)
100
+ attr_accessor :failures
101
+ attr_accessor :skipped
102
+
103
+ def initialize(*args)
104
+ super
105
+ @failures = []
106
+ end
107
+
108
+ # Starts timing the test.
109
+ def start
110
+ @start = Time.now
111
+ end
112
+
113
+ # Finishes timing the test.
114
+ def finish
115
+ self.time = Time.now - @start
116
+ end
117
+
118
+ # Returns non-nil if the test failed.
119
+ def failure?
120
+ !failures.empty? && failures.detect {|f| f.failure? }
121
+ end
122
+
123
+ # Returns non-nil if the test had an error.
124
+ def error?
125
+ !failures.empty? && failures.detect {|f| f.error? }
126
+ end
127
+
128
+ def skipped?
129
+ return skipped
130
+ end
131
+
132
+ # Writes xml representing the test result to the provided builder.
133
+ def to_xml(builder)
134
+ attrs = {}
135
+ each_pair {|k,v| attrs[k] = builder.trunc!(v.to_s) unless v.nil? || v.to_s.empty?}
136
+ builder.testcase(attrs) do
137
+ if skipped
138
+ builder.skipped
139
+ else
140
+ failures.each do |failure|
141
+ builder.failure(:type => builder.trunc!(failure.name), :message => builder.trunc!(failure.message)) do
142
+ builder.text!(failure.message + " (#{failure.name})\n")
143
+ builder.text!(failure.location)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,132 @@
1
+ # Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require 'ci/reporter/core'
6
+ require 'test/unit'
7
+ require 'test/unit/ui/console/testrunner'
8
+
9
+ module CI
10
+ module Reporter
11
+ # Factory for constructing either a CI::Reporter::TestUnitFailure or CI::Reporter::TestUnitError depending on the result
12
+ # of the test.
13
+ class Failure
14
+ def self.new(fault)
15
+ return TestUnitFailure.new(fault) if fault.kind_of?(Test::Unit::Failure)
16
+ return TestUnitSkipped.new(fault) if Test::Unit.constants.include?("Omission") && (fault.kind_of?(Test::Unit::Omission) || fault.kind_of?(Test::Unit::Pending))
17
+ TestUnitError.new(fault)
18
+ end
19
+ end
20
+
21
+ # Wrapper around a <code>Test::Unit</code> error to be used by the test suite to interpret results.
22
+ class TestUnitError
23
+ def initialize(fault) @fault = fault end
24
+ def failure?() false end
25
+ def error?() true end
26
+ def name() @fault.exception.class.name end
27
+ def message() @fault.exception.message end
28
+ def location() @fault.exception.backtrace.join("\n") end
29
+ end
30
+
31
+ # Wrapper around a <code>Test::Unit</code> failure to be used by the test suite to interpret results.
32
+ class TestUnitFailure
33
+ def initialize(fault) @fault = fault end
34
+ def failure?() true end
35
+ def error?() false end
36
+ def name() Test::Unit::AssertionFailedError.name end
37
+ def message() @fault.message end
38
+ def location() @fault.location.join("\n") end
39
+ end
40
+
41
+ # Wrapper around a <code>Test::Unit</code> 2.0 omission.
42
+ class TestUnitSkipped
43
+ def initialize(fault) @fault = fault end
44
+ def failure?() false end
45
+ def error?() false end
46
+ def name() Test::Unit::Omission.name end
47
+ def message() @fault.message end
48
+ def location() @fault.location.join("\n") end
49
+ end
50
+
51
+ # Replacement Mediator that adds listeners to capture the results of the <code>Test::Unit</code> runs.
52
+ class TestUnit < Test::Unit::UI::TestRunnerMediator
53
+ def initialize(suite, report_mgr = nil)
54
+ super(suite)
55
+ @report_manager = report_mgr || ReportManager.new("test")
56
+ add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:started))
57
+ add_listener(Test::Unit::TestCase::STARTED, &method(:test_started))
58
+ add_listener(Test::Unit::TestCase::FINISHED, &method(:test_finished))
59
+ add_listener(Test::Unit::TestResult::FAULT, &method(:fault))
60
+ add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:finished))
61
+ end
62
+
63
+ def started(result)
64
+ @suite_result = result
65
+ @last_assertion_count = 0
66
+ @current_suite = nil
67
+ @unknown_count = 0
68
+ @result_assertion_count = 0
69
+ end
70
+
71
+ def test_started(name)
72
+ test_name, suite_name = extract_names(name)
73
+ unless @current_suite && @current_suite.name == suite_name
74
+ finish_suite
75
+ start_suite(suite_name)
76
+ end
77
+ start_test(test_name)
78
+ end
79
+
80
+ def test_finished(name)
81
+ finish_test
82
+ end
83
+
84
+ def fault(fault)
85
+ tc = @current_suite.testcases.last
86
+ tc.failures << Failure.new(fault)
87
+ end
88
+
89
+ def finished(elapsed_time)
90
+ finish_suite
91
+ end
92
+
93
+ private
94
+ def extract_names(name)
95
+ match = name.match(/(.*)\(([^)]*)\)/)
96
+ if match
97
+ [match[1], match[2]]
98
+ else
99
+ @unknown_count += 1
100
+ [name, "unknown-#{@unknown_count}"]
101
+ end
102
+ end
103
+
104
+ def start_suite(suite_name)
105
+ @current_suite = TestSuite.new(suite_name)
106
+ @current_suite.start
107
+ end
108
+
109
+ def finish_suite
110
+ if @current_suite
111
+ @current_suite.finish
112
+ @current_suite.assertions = @suite_result.assertion_count - @last_assertion_count
113
+ @last_assertion_count = @suite_result.assertion_count
114
+ @report_manager.write_report(@current_suite)
115
+ end
116
+ end
117
+
118
+ def start_test(test_name)
119
+ tc = TestCase.new(test_name)
120
+ tc.start
121
+ @current_suite.testcases << tc
122
+ end
123
+
124
+ def finish_test
125
+ tc = @current_suite.testcases.last
126
+ tc.finish
127
+ tc.assertions = @suite_result.assertion_count - @result_assertion_count
128
+ @result_assertion_count = @suite_result.assertion_count
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,5 @@
1
+ module CI
2
+ module Reporter
3
+ VERSION = "1.6.5"
4
+ end
5
+ end
@@ -0,0 +1,230 @@
1
+ # Copyright (c) 2006-2010 Nick Sieger <nicksieger@gmail.com>
2
+ # See the file LICENSE.txt included with the distribution for
3
+ # software license details.
4
+
5
+ require File.dirname(__FILE__) + "/../../spec_helper.rb"
6
+ require 'ci/reporter/cucumber'
7
+
8
+ describe "The Cucumber reporter" do
9
+ describe CI::Reporter::CucumberFailure do
10
+ before(:each) do
11
+ @klass = mock("class")
12
+ @klass.stub!(:name).and_return("Exception name")
13
+
14
+ @exception = mock("exception")
15
+ @exception.stub!(:class).and_return(@klass)
16
+ @exception.stub!(:message).and_return("Exception message")
17
+ @exception.stub!(:backtrace).and_return(["First line", "Second line"])
18
+
19
+ @step = mock("step")
20
+ @step.stub!(:exception).and_return(@exception)
21
+
22
+ @cucumber_failure = CI::Reporter::CucumberFailure.new(@step)
23
+ end
24
+
25
+ it "should always return true for failure?" do
26
+ @cucumber_failure.should be_failure
27
+ end
28
+
29
+ it "should always return false for error?" do
30
+ @cucumber_failure.should_not be_error
31
+ end
32
+
33
+ it "should propagate the name as the underlying exception's class name" do
34
+ @step.should_receive(:exception)
35
+ @exception.should_receive(:class)
36
+ @klass.should_receive(:name)
37
+
38
+ @cucumber_failure.name.should == "Exception name"
39
+ end
40
+
41
+ it "should propagate the message as the underlying exception's message" do
42
+ @step.should_receive(:exception)
43
+ @exception.should_receive(:message)
44
+
45
+ @cucumber_failure.message.should == "Exception message"
46
+ end
47
+
48
+ it "should propagate and format the exception's backtrace" do
49
+ @step.should_receive(:exception)
50
+ @exception.should_receive(:backtrace)
51
+
52
+ @cucumber_failure.location.should == "First line\nSecond line"
53
+ end
54
+ end
55
+
56
+ describe CI::Reporter::Cucumber do
57
+ before(:each) do
58
+ @step_mother = mock("step_mother")
59
+ @io = mock("io")
60
+
61
+ @report_manager = mock("report_manager")
62
+ CI::Reporter::ReportManager.stub!(:new).and_return(@report_manager)
63
+ end
64
+
65
+ def new_instance
66
+ CI::Reporter::Cucumber.new(@step_mother, @io, {})
67
+ end
68
+
69
+ it "should create a new report manager to report on test success/failure" do
70
+ CI::Reporter::ReportManager.should_receive(:new)
71
+ new_instance
72
+ end
73
+
74
+ it "should record the feature name when a new feature is visited" do
75
+ cucumber = new_instance
76
+ cucumber.feature_name(nil, "Some feature name")
77
+ cucumber.name.should == "Some feature name"
78
+ end
79
+
80
+ it "should record only the first line of a feature name" do
81
+ cucumber = new_instance
82
+ cucumber.feature_name(nil, "Some feature name\nLonger description")
83
+ cucumber.name.should == "Some feature name"
84
+ end
85
+
86
+ context "applied to a feature" do
87
+ before(:each) do
88
+ @cucumber = new_instance
89
+ @cucumber.feature_name(nil, "Demo feature")
90
+
91
+ @test_suite = mock("test_suite", :start => nil, :finish => nil, :name= => nil)
92
+ CI::Reporter::TestSuite.stub!(:new).and_return(@test_suite)
93
+
94
+ @feature = mock("feature")
95
+
96
+ @report_manager.stub!(:write_report)
97
+ end
98
+
99
+ context "before" do
100
+ it "should create a new test suite" do
101
+ CI::Reporter::TestSuite.should_receive(:new).with(/Demo feature/)
102
+ @cucumber.before_feature(@feature)
103
+ end
104
+
105
+ it "should indicate that the test suite has started" do
106
+ @test_suite.should_receive(:start)
107
+ @cucumber.before_feature(@feature)
108
+ end
109
+ end
110
+
111
+ context "after" do
112
+ before :each do
113
+ @cucumber = new_instance
114
+ @cucumber.feature_name(nil, "Demo feature")
115
+
116
+ @test_suite = mock("test_suite", :start => nil, :finish => nil, :name= => nil)
117
+ CI::Reporter::TestSuite.stub!(:new).and_return(@test_suite)
118
+
119
+ @feature = mock("feature")
120
+
121
+ @report_manager.stub!(:write_report)
122
+
123
+ @cucumber.before_feature(@feature)
124
+ end
125
+
126
+ it "should indicate that the test suite has finished" do
127
+ @test_suite.should_receive(:finish)
128
+ @cucumber.after_feature(@feature)
129
+ end
130
+
131
+ it "should ask the report manager to write a report" do
132
+ @report_manager.should_receive(:write_report).with(@test_suite)
133
+ @cucumber.after_feature(@feature)
134
+ end
135
+ end
136
+ end
137
+
138
+ context "inside a scenario" do
139
+ before(:each) do
140
+ @testcases = []
141
+
142
+ @test_suite = mock("test_suite", :testcases => @testcases)
143
+
144
+ @cucumber = new_instance
145
+ @cucumber.stub!(:test_suite).and_return(@test_suite)
146
+
147
+ @test_case = mock("test_case", :start => nil, :finish => nil, :name => "Step Name")
148
+ CI::Reporter::TestCase.stub!(:new).and_return(@test_case)
149
+
150
+ @step = mock("step", :status => :passed)
151
+ @step.stub!(:name).and_return("Step Name")
152
+ end
153
+
154
+ context "before steps" do
155
+ it "should create a new test case" do
156
+ CI::Reporter::TestCase.should_receive(:new).with("Step Name")
157
+ @cucumber.scenario_name(nil, "Step Name")
158
+ @cucumber.before_steps(@step)
159
+ end
160
+
161
+ it "should indicate that the test case has started" do
162
+ @test_case.should_receive(:start)
163
+ @cucumber.before_steps(@step)
164
+ end
165
+ end
166
+
167
+ context "after steps" do
168
+ before :each do
169
+ @cucumber.before_steps(@step)
170
+ end
171
+
172
+ it "should indicate that the test case has finished" do
173
+ @test_case.should_receive(:finish)
174
+ @cucumber.after_steps(@step)
175
+ end
176
+
177
+ it "should add the test case to the suite's list of cases" do
178
+ @testcases.should be_empty
179
+ @cucumber.after_steps(@step)
180
+ @testcases.should_not be_empty
181
+ @testcases.first.should == @test_case
182
+ end
183
+
184
+ it "should alter the name of a test case that is pending to include '(PENDING)'" do
185
+ @step.stub!(:status).and_return(:pending)
186
+ @test_case.should_receive(:name=).with("Step Name (PENDING)")
187
+ @cucumber.after_steps(@step)
188
+ end
189
+
190
+ it "should alter the name of a test case that is undefined to include '(PENDING)'" do
191
+ @step.stub!(:status).and_return(:undefined)
192
+ @test_case.should_receive(:name=).with("Step Name (PENDING)")
193
+ @cucumber.after_steps(@step)
194
+ end
195
+
196
+ it "should alter the name of a test case that was skipped to include '(SKIPPED)'" do
197
+ @step.stub!(:status).and_return(:skipped)
198
+ @test_case.should_receive(:name=).with("Step Name (SKIPPED)")
199
+ @cucumber.after_steps(@step)
200
+ end
201
+ end
202
+
203
+ describe "that fails" do
204
+ before(:each) do
205
+ @step.stub!(:status).and_return(:failed)
206
+
207
+ @failures = []
208
+ @test_case.stub!(:failures).and_return(@failures)
209
+
210
+ @cucumber.before_steps(@step)
211
+
212
+ @cucumber_failure = mock("cucumber_failure")
213
+ CI::Reporter::CucumberFailure.stub!(:new).and_return(@cucumber_failure)
214
+ end
215
+
216
+ it "should create a new cucumber failure with that step" do
217
+ CI::Reporter::CucumberFailure.should_receive(:new).with(@step)
218
+ @cucumber.after_steps(@step)
219
+ end
220
+
221
+ it "should add the failure to the suite's list of failures" do
222
+ @failures.should be_empty
223
+ @cucumber.after_steps(@step)
224
+ @failures.should_not be_empty
225
+ @failures.first.should == @cucumber_failure
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end