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.
- data/History.txt +136 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +29 -0
- data/README.txt +65 -0
- data/Rakefile +101 -0
- data/lib/ci/reporter/core.rb +6 -0
- data/lib/ci/reporter/cucumber.rb +120 -0
- data/lib/ci/reporter/rake/cucumber.rb +19 -0
- data/lib/ci/reporter/rake/cucumber_loader.rb +6 -0
- data/lib/ci/reporter/rake/rspec.rb +25 -0
- data/lib/ci/reporter/rake/rspec_loader.rb +6 -0
- data/lib/ci/reporter/rake/test_unit.rb +15 -0
- data/lib/ci/reporter/rake/test_unit_loader.rb +21 -0
- data/lib/ci/reporter/rake/utils.rb +14 -0
- data/lib/ci/reporter/report_manager.rb +34 -0
- data/lib/ci/reporter/rspec.rb +189 -0
- data/lib/ci/reporter/test_suite.rb +151 -0
- data/lib/ci/reporter/test_unit.rb +132 -0
- data/lib/ci/reporter/version.rb +5 -0
- data/spec/ci/reporter/cucumber_spec.rb +230 -0
- data/spec/ci/reporter/output_capture_spec.rb +57 -0
- data/spec/ci/reporter/rake/rake_tasks_spec.rb +100 -0
- data/spec/ci/reporter/report_manager_spec.rb +51 -0
- data/spec/ci/reporter/rspec_spec.rb +146 -0
- data/spec/ci/reporter/test_suite_spec.rb +151 -0
- data/spec/ci/reporter/test_unit_spec.rb +152 -0
- data/spec/spec_helper.rb +21 -0
- data/stub.rake +14 -0
- data/tasks/ci_reporter.rake +18 -0
- metadata +124 -0
@@ -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,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
|