finnlabs-ci_reporter 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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