mcmire-protest 0.2.4
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/.gitignore +2 -0
- data/LICENSE +22 -0
- data/README.rdoc +200 -0
- data/Rakefile +24 -0
- data/lib/protest/rails.rb +80 -0
- data/lib/protest/report.rb +121 -0
- data/lib/protest/reports/documentation.rb +77 -0
- data/lib/protest/reports/progress.rb +46 -0
- data/lib/protest/reports.rb +2 -0
- data/lib/protest/runner.rb +39 -0
- data/lib/protest/test_case.rb +248 -0
- data/lib/protest/tests.rb +90 -0
- data/lib/protest/utils/backtrace_filter.rb +25 -0
- data/lib/protest/utils/colorful_output.rb +67 -0
- data/lib/protest/utils/summaries.rb +129 -0
- data/lib/protest/utils.rb +6 -0
- data/lib/protest.rb +108 -0
- data/protest.gemspec +38 -0
- metadata +72 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2009 Nicolas Sanguinetti
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
= Protest, the simplicity rebel test framework
|
2
|
+
|
3
|
+
require "protest"
|
4
|
+
|
5
|
+
Protest.context("A user") do
|
6
|
+
setup do
|
7
|
+
@user = User.new(:name => "John Doe", :email => "john@example.org")
|
8
|
+
end
|
9
|
+
|
10
|
+
test "has a name" do
|
11
|
+
assert_equal "John Doe", @user.name
|
12
|
+
end
|
13
|
+
|
14
|
+
test "has an email" do
|
15
|
+
assert_equal "john@example.org", @user.email
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Protest is a small, simple, and easy-to-extend testing framework for ruby. It
|
20
|
+
was written as a replacement for Test::Unit, given how awful its code is, and
|
21
|
+
how difficult it is to extend in order to add new features.
|
22
|
+
|
23
|
+
I believe in minimalistic software, which is easily understood, easy to test,
|
24
|
+
and specially, easy to extend for third parties. That's where I'm aiming with
|
25
|
+
Protest.
|
26
|
+
|
27
|
+
== Get it
|
28
|
+
|
29
|
+
gem install protest
|
30
|
+
|
31
|
+
Or
|
32
|
+
|
33
|
+
rip install git://github.com/foca/protest.git v0.2.3
|
34
|
+
|
35
|
+
== Setup and teardown
|
36
|
+
|
37
|
+
If you need to run code before or after each test, declare a +setup+ or
|
38
|
+
+teardown+ block (respectively.)
|
39
|
+
|
40
|
+
Protest.context("A user") do
|
41
|
+
setup do # this runs before each test
|
42
|
+
@user = User.create(:name => "John")
|
43
|
+
end
|
44
|
+
|
45
|
+
teardown do # this runs after each test
|
46
|
+
@user.destroy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
+setup+ and +teardown+ blocks are evaluated in the same context as your test,
|
51
|
+
which means any instance variables defined in any of them are available in the
|
52
|
+
rest.
|
53
|
+
|
54
|
+
You can also use +global_setup+ and +global_teardown+ to run code only once per
|
55
|
+
test case. +global_setup+ blocks will run once before the first test is run, and
|
56
|
+
+global_teardown+ will run after all the tests have been run.
|
57
|
+
|
58
|
+
These methods, however, are dangerous, and should be used with caution, as
|
59
|
+
they might introduce dependencies between your tests if you don't write
|
60
|
+
your tests properly. Make sure that any state modified by code run in a
|
61
|
+
+global_setup+ or +global_teardown+ isn't changed in any of your tests.
|
62
|
+
|
63
|
+
Also, you should be aware that the code of +global_setup+ and +global_teardown+
|
64
|
+
blocks isn't evaluated in the same context as your tests and normal
|
65
|
+
+setup+/+teardown+ blocks are, so you can't share instance variables between
|
66
|
+
them.
|
67
|
+
|
68
|
+
== Nested contexts
|
69
|
+
|
70
|
+
Break down your test into logical chunks with nested contexts:
|
71
|
+
|
72
|
+
Protest.context("A user") do
|
73
|
+
setup do
|
74
|
+
@user = User.make
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when validating" do
|
78
|
+
test "validates name" do
|
79
|
+
@user.name = nil
|
80
|
+
assert !@user.valid?
|
81
|
+
end
|
82
|
+
|
83
|
+
# etc, etc
|
84
|
+
end
|
85
|
+
|
86
|
+
context "doing something else" do
|
87
|
+
# your get the idea
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Any +setup+ or +teardown+ blocks you defined in a context will run in that
|
92
|
+
context and in _any_ other context nested in it.
|
93
|
+
|
94
|
+
== Pending tests
|
95
|
+
|
96
|
+
There are two ways of marking a test as pending. You can declare a test with no
|
97
|
+
body:
|
98
|
+
|
99
|
+
Protest.context("Some tests") do
|
100
|
+
test "this test will be marked as pending"
|
101
|
+
|
102
|
+
test "this tests is also pending"
|
103
|
+
|
104
|
+
test "this test isn't pending" do
|
105
|
+
assert true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Or you can call the +pending+ method from inside your test.
|
110
|
+
|
111
|
+
Protest.context("Some tests") do
|
112
|
+
test "this test is pending" do
|
113
|
+
pending "oops, this doesn't work"
|
114
|
+
assert false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
== Custom assertions
|
119
|
+
|
120
|
+
By default Protest bundles all the assertions defined in Test::Unit (it
|
121
|
+
literally requires them), so check its documentation for all the goodness.
|
122
|
+
|
123
|
+
If you want to add assertions, just define methods that rely on +assert+ or
|
124
|
+
+assert_block+. The former takes a boolean and an optional error message as
|
125
|
+
arguments, while the latter takes an optional error message as an argument and
|
126
|
+
a block. The assertions is considered to fail if the block evaluates to neither
|
127
|
+
+false+ nor +nil+.
|
128
|
+
|
129
|
+
For example:
|
130
|
+
|
131
|
+
module AwesomenessAssertions
|
132
|
+
def assert_awesomeness(object)
|
133
|
+
assert object.awesome?, "#{object.inspect} is not awesome enough"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Protest::TestCase
|
138
|
+
include AwesomenessAssertions
|
139
|
+
end
|
140
|
+
|
141
|
+
You could also define rspec-like matchers if you like that style. See
|
142
|
+
<tt>matchers.rb</tt> in the examples directory for an example.
|
143
|
+
|
144
|
+
== Reports
|
145
|
+
|
146
|
+
Protest can report the output of a test suite in many ways. The library ships
|
147
|
+
with a <tt>:progress</tt> report, and a <tt>:documentation</tt> report,
|
148
|
+
<tt>:progress</tt> being the default.
|
149
|
+
|
150
|
+
=== Progress report
|
151
|
+
|
152
|
+
This is the default option, but you can force this by calling
|
153
|
+
<tt>Protest.report_with(:progress)</tt>.
|
154
|
+
|
155
|
+
The progress report will output the "classic" Test::Unit output of periods for
|
156
|
+
passing tests, "F" for failing assertions, "E" for unrescued exceptions, and
|
157
|
+
"P" for pending tests, in full color.
|
158
|
+
|
159
|
+
=== Documentation report
|
160
|
+
|
161
|
+
Use this report by calling <tt>Protest.report_with(:documentation)</tt>
|
162
|
+
|
163
|
+
For each testcase in your suite, this will output the description of the test
|
164
|
+
case (whatever you provide TestCase.context), followed by the name of each test
|
165
|
+
in that context, one per line. For example:
|
166
|
+
|
167
|
+
Protest.context "A user" do
|
168
|
+
test "has a name"
|
169
|
+
test "has an email"
|
170
|
+
|
171
|
+
context "validations" do
|
172
|
+
test "ensure the email can't be blank"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
Will output, when run with the <tt>:documentation</tt> report:
|
177
|
+
|
178
|
+
A user
|
179
|
+
- has a name (Not Yet Implemented)
|
180
|
+
- has an email (Not Yet Implemented)
|
181
|
+
|
182
|
+
A user validations
|
183
|
+
- ensure the email can't be blank (Not Yet Implemented)
|
184
|
+
|
185
|
+
(The 'Not Yet Implemented' messages are because the tests have no body. See
|
186
|
+
"Pending tests", above.)
|
187
|
+
|
188
|
+
This is similar to the specdoc runner in rspec[http://rspec.info].
|
189
|
+
|
190
|
+
=== Defining your own reports
|
191
|
+
|
192
|
+
This is really, really easy. All you need to do is subclass Report, and
|
193
|
+
register your subclass by calling +Protest.add_report+. See the
|
194
|
+
documentation for details, or take a look at the source code for
|
195
|
+
Protest::Reports::Progress and Protest::Reports::Documentation.
|
196
|
+
|
197
|
+
== Legal
|
198
|
+
|
199
|
+
Author:: Nicolás Sanguinetti — http://nicolassanguinetti.info
|
200
|
+
License:: MIT (see bundled LICENSE file for more info)
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
begin
|
2
|
+
require "hanna/rdoctask"
|
3
|
+
rescue LoadError
|
4
|
+
require "rake/rdoctask"
|
5
|
+
end
|
6
|
+
|
7
|
+
require "rake/testtask"
|
8
|
+
|
9
|
+
Rake::RDocTask.new do |rd|
|
10
|
+
rd.main = "README.rdoc"
|
11
|
+
rd.title = "API Documentation for Protest"
|
12
|
+
rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb")
|
13
|
+
rd.rdoc_dir = "doc"
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
require "mg"
|
18
|
+
MG.new("protest.gemspec")
|
19
|
+
rescue LoadError
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::TestTask.new
|
23
|
+
|
24
|
+
task :default => :test
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "protest"
|
2
|
+
require "test/unit/assertions"
|
3
|
+
require "action_controller/test_case"
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "webrat"
|
7
|
+
rescue LoadError
|
8
|
+
$no_webrat = true
|
9
|
+
end
|
10
|
+
|
11
|
+
module Protest
|
12
|
+
module Rails
|
13
|
+
# Exclude rails' files from the errors
|
14
|
+
class BacktraceFilter < Utils::BacktraceFilter
|
15
|
+
include ::Rails::BacktraceFilterForTestUnit
|
16
|
+
|
17
|
+
def filter_backtrace(backtrace, prefix=nil)
|
18
|
+
super(backtrace, prefix).reject do |line|
|
19
|
+
line.starts_with?("/")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Wrap all tests in a database transaction.
|
25
|
+
#
|
26
|
+
# TODO: make this optional somehow (yet enabled by default) so users of
|
27
|
+
# other ORMs don't run into problems.
|
28
|
+
module TransactionalTests
|
29
|
+
def run(*args, &block)
|
30
|
+
ActiveRecord::Base.connection.transaction do
|
31
|
+
super(*args, &block)
|
32
|
+
raise ActiveRecord::Rollback
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# You should inherit from this TestCase in order to get rails' helpers
|
38
|
+
# loaded into Protest. These include all the assertions bundled with rails
|
39
|
+
# and your tests being wrapped in a transaction.
|
40
|
+
class TestCase < ::Protest::TestCase
|
41
|
+
include ::Test::Unit::Assertions
|
42
|
+
include ActiveSupport::Testing::Assertions
|
43
|
+
include TransactionalTests
|
44
|
+
end
|
45
|
+
|
46
|
+
class RequestTest < TestCase #:nodoc:
|
47
|
+
%w(response selector tag dom routing model).each do |kind|
|
48
|
+
include ActionController::Assertions.const_get("#{kind.camelize}Assertions")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Make your integration tests inherit from this class, which bundles the
|
53
|
+
# integration runner included with rails, and webrat's test methods. You
|
54
|
+
# should use webrat for integration tests. Really.
|
55
|
+
class IntegrationTest < RequestTest
|
56
|
+
include ActionController::Integration::Runner
|
57
|
+
include Webrat::Methods unless $no_webrat
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# The preferred way to declare a context (top level) is to use
|
62
|
+
# +Protest.describe+ or +Protest.context+, which will ensure you're using
|
63
|
+
# rails adapter with the helpers you need.
|
64
|
+
def self.context(description, &block)
|
65
|
+
Rails::TestCase.context(description, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Use +Protest.story+ to declare an integration test for your rails app. Note
|
69
|
+
# that the files should still be called 'test/integration/foo_test.rb' if you
|
70
|
+
# want the 'test:integration' rake task to pick them up.
|
71
|
+
def self.story(description, &block)
|
72
|
+
Rails::IntegrationTest.story(description, &block)
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
alias_method :describe, :context
|
77
|
+
end
|
78
|
+
|
79
|
+
self.backtrace_filter = Rails::BacktraceFilter.new
|
80
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Protest
|
2
|
+
class Report
|
3
|
+
# Define an event handler for your report. The different events fired in a
|
4
|
+
# report's life cycle are:
|
5
|
+
#
|
6
|
+
# :start:: Fired by the runner when starting the whole test suite.
|
7
|
+
# :enter:: Fired by the runner when starting a particular test case. It
|
8
|
+
# will get the test case as an argument.
|
9
|
+
# :test:: Fired by a test before it starts running. It will get the
|
10
|
+
# instance of TestCase for the given test as an argument.
|
11
|
+
# :assertion:: Fired by a test each time an assertion is run.
|
12
|
+
# :pass:: Fired by a test after it runs successfully without errors.
|
13
|
+
# It will get an instance of PassedTest as an argument.
|
14
|
+
# :pending:: Fired by a test which doesn't provide a test block or which
|
15
|
+
# calls TestCase#pending. It will get an instance of
|
16
|
+
# PendingTest as an argument.
|
17
|
+
# :failure:: Fired by a test in which an assertion failed. It will get an
|
18
|
+
# instance of FailedTest as an argument.
|
19
|
+
# :error:: Fired by a test where an uncaught exception was found. It
|
20
|
+
# will get an instance of ErroredTest as an argument.
|
21
|
+
# :exit:: Fired by the runner each time a test case finishes. It will
|
22
|
+
# take the test case as an argument.
|
23
|
+
# :end:: Fired by the runner at the end of the whole test suite.
|
24
|
+
#
|
25
|
+
# The event handler will receive the report as a first argument, plus any
|
26
|
+
# arguments documented above (depending on the event). It will also ensure
|
27
|
+
# that any handler for the same event declared on an ancestor class is run.
|
28
|
+
def self.on(event, &block)
|
29
|
+
define_method(:"on_#{event}") do |*args|
|
30
|
+
begin
|
31
|
+
super(*args)
|
32
|
+
rescue NoMethodError
|
33
|
+
end
|
34
|
+
|
35
|
+
block.call(self, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
on :test do |report, test|
|
40
|
+
report.tests << test
|
41
|
+
end
|
42
|
+
|
43
|
+
on :start do |report|
|
44
|
+
report.instance_eval { @started_at = Time.now }
|
45
|
+
end
|
46
|
+
|
47
|
+
on :pass do |report, passed_test|
|
48
|
+
report.passes << passed_test
|
49
|
+
end
|
50
|
+
|
51
|
+
on :pending do |report, pending_test|
|
52
|
+
report.pendings << pending_test
|
53
|
+
end
|
54
|
+
|
55
|
+
on :failure do |report, failed_test|
|
56
|
+
report.failures << failed_test
|
57
|
+
report.failures_and_errors << failed_test
|
58
|
+
end
|
59
|
+
|
60
|
+
on :error do |report, errored_test|
|
61
|
+
report.errors << errored_test
|
62
|
+
report.failures_and_errors << errored_test
|
63
|
+
end
|
64
|
+
|
65
|
+
on :assertion do |report|
|
66
|
+
report.add_assertion
|
67
|
+
end
|
68
|
+
|
69
|
+
# List all the tests (as PendingTest instances) that were pending.
|
70
|
+
def pendings
|
71
|
+
@pendings ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
# List all the tests (as PassedTest instances) that passed.
|
75
|
+
def passes
|
76
|
+
@passes ||= []
|
77
|
+
end
|
78
|
+
|
79
|
+
# List all the tests (as FailedTest instances) that failed an assertion.
|
80
|
+
def failures
|
81
|
+
@failures ||= []
|
82
|
+
end
|
83
|
+
|
84
|
+
# List all the tests (as ErroredTest instances) that raised an unrescued
|
85
|
+
# exception.
|
86
|
+
def errors
|
87
|
+
@errors ||= []
|
88
|
+
end
|
89
|
+
|
90
|
+
# Aggregated and ordered list of tests that either failed an assertion or
|
91
|
+
# raised an unrescued exception. Useful for displaying back to the user.
|
92
|
+
def failures_and_errors
|
93
|
+
@failures_and_errors ||= []
|
94
|
+
end
|
95
|
+
|
96
|
+
# Log an assertion was run (whether it succeeded or failed.)
|
97
|
+
def add_assertion
|
98
|
+
@assertions ||= 0
|
99
|
+
@assertions += 1
|
100
|
+
end
|
101
|
+
|
102
|
+
# Number of assertions run during the report.
|
103
|
+
def assertions
|
104
|
+
@assertions || 0
|
105
|
+
end
|
106
|
+
|
107
|
+
def tests
|
108
|
+
@tests ||= []
|
109
|
+
end
|
110
|
+
|
111
|
+
# Amount ot tests run (whether passed, pending, failed, or errored.)
|
112
|
+
def total_tests
|
113
|
+
tests.size
|
114
|
+
end
|
115
|
+
|
116
|
+
# Seconds taken since the test suite started running
|
117
|
+
def time_elapsed
|
118
|
+
Time.now - @started_at
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Protest
|
2
|
+
# For each testcase in your suite, this will output the description of the test
|
3
|
+
# case (whatever you provide TestCase.context), followed by the name of each test
|
4
|
+
# in that context, one per line. For example:
|
5
|
+
#
|
6
|
+
# Protest.context "A user" do
|
7
|
+
# test "has a name" do
|
8
|
+
# ...
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# test "has an email" do
|
12
|
+
# ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# context "validations" do
|
16
|
+
# test "ensure the email can't be blank" do
|
17
|
+
# ...
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Will output, when run with the +:documentation+ report:
|
23
|
+
#
|
24
|
+
# A user
|
25
|
+
# - has a name
|
26
|
+
# - has an email
|
27
|
+
#
|
28
|
+
# A user validations
|
29
|
+
# - ensure the email can't be blank
|
30
|
+
#
|
31
|
+
# This is based on the specdoc runner in rspec[http://rspec.info].
|
32
|
+
class Reports::Documentation < Report
|
33
|
+
include Utils::Summaries
|
34
|
+
include Utils::ColorfulOutput
|
35
|
+
|
36
|
+
attr_reader :stream #:nodoc:
|
37
|
+
|
38
|
+
# Set the stream where the report will be written to. STDOUT by default.
|
39
|
+
def initialize(stream=STDOUT)
|
40
|
+
@stream = stream
|
41
|
+
end
|
42
|
+
|
43
|
+
on :enter do |report, context|
|
44
|
+
report.puts context.description unless context.tests.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
on :pass do |report, passed_test|
|
48
|
+
report.puts "- #{passed_test.test_name}", :passed
|
49
|
+
end
|
50
|
+
|
51
|
+
on :failure do |report, failed_test|
|
52
|
+
position = report.failures_and_errors.index(failed_test) + 1
|
53
|
+
report.puts "- #{failed_test.test_name} (#{position})", :failed
|
54
|
+
end
|
55
|
+
|
56
|
+
on :error do |report, errored_test|
|
57
|
+
position = report.failures_and_errors.index(errored_test) + 1
|
58
|
+
report.puts "- #{errored_test.test_name} (#{position})", :errored
|
59
|
+
end
|
60
|
+
|
61
|
+
on :pending do |report, pending_test|
|
62
|
+
report.puts "- #{pending_test.test_name} (#{pending_test.pending_message})", :pending
|
63
|
+
end
|
64
|
+
|
65
|
+
on :exit do |report, context|
|
66
|
+
report.puts unless context.tests.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
on :end do |report|
|
70
|
+
report.summarize_pending_tests
|
71
|
+
report.summarize_errors
|
72
|
+
report.summarize_test_totals
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
add_report :documentation, Reports::Documentation
|
77
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Protest
|
2
|
+
# The +:progress+ report will output a +.+ for each passed test in the suite,
|
3
|
+
# a +P+ for each pending test, an +F+ for each test that failed an assertion,
|
4
|
+
# and an +E+ for each test that raised an unrescued exception.
|
5
|
+
#
|
6
|
+
# At the end of the suite it will output a list of all pending tests, with
|
7
|
+
# files and line numbers, and after that a list of all failures and errors,
|
8
|
+
# which also contains the first 3 lines of the backtrace for each.
|
9
|
+
class Reports::Progress < Report
|
10
|
+
include Utils::Summaries
|
11
|
+
include Utils::ColorfulOutput
|
12
|
+
|
13
|
+
attr_reader :stream #:nodoc:
|
14
|
+
|
15
|
+
# Set the stream where the report will be written to. STDOUT by default.
|
16
|
+
def initialize(stream=STDOUT)
|
17
|
+
@stream = stream
|
18
|
+
end
|
19
|
+
|
20
|
+
on :end do |report|
|
21
|
+
report.puts
|
22
|
+
report.puts
|
23
|
+
report.summarize_pending_tests
|
24
|
+
report.summarize_errors
|
25
|
+
report.summarize_test_totals
|
26
|
+
end
|
27
|
+
|
28
|
+
on :pass do |report, pass|
|
29
|
+
report.print ".", :passed
|
30
|
+
end
|
31
|
+
|
32
|
+
on :pending do |report, pending|
|
33
|
+
report.print "P", :pending
|
34
|
+
end
|
35
|
+
|
36
|
+
on :failure do |report, failure|
|
37
|
+
report.print "F", :failed
|
38
|
+
end
|
39
|
+
|
40
|
+
on :error do |report, error|
|
41
|
+
report.print "E", :errored
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
add_report :progress, Reports::Progress
|
46
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Protest
|
2
|
+
class Runner
|
3
|
+
# Set up the test runner. Takes in a report that will be passed to test
|
4
|
+
# cases for reporting.
|
5
|
+
def initialize(report)
|
6
|
+
@report = report
|
7
|
+
end
|
8
|
+
|
9
|
+
# Run a set of test cases, provided as arguments. This will fire relevant
|
10
|
+
# events on the runner's report, at the +start+ and +end+ of the test run,
|
11
|
+
# and before and after each test case (+enter+ and +exit+.)
|
12
|
+
def run(*test_cases)
|
13
|
+
@report.on_start if @report.respond_to?(:on_start)
|
14
|
+
test_cases.each do |test_case|
|
15
|
+
@report.on_enter(test_case) if @report.respond_to?(:on_enter)
|
16
|
+
test_case.run(self)
|
17
|
+
@report.on_exit(test_case) if @report.respond_to?(:on_exit)
|
18
|
+
end
|
19
|
+
@report.on_end if @report.respond_to?(:on_end)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Run a test and report if it passes, fails, or is pending. Takes the name
|
23
|
+
# of the test as an argument. By passing +true+ as the second argument, you
|
24
|
+
# force any exceptions to be re-raied and the test not reported as a pass
|
25
|
+
# after it finishes (for global setup/teardown blocks)
|
26
|
+
def report(test, running_global_setup_or_teardown=false)
|
27
|
+
@report.on_test(Test.new(test)) if @report.respond_to?(:on_test) && !running_global_setup_or_teardown
|
28
|
+
test.run(@report)
|
29
|
+
@report.on_pass(PassedTest.new(test)) unless running_global_setup_or_teardown
|
30
|
+
rescue Pending => e
|
31
|
+
@report.on_pending(PendingTest.new(test, e))
|
32
|
+
rescue AssertionFailed => e
|
33
|
+
@report.on_failure(FailedTest.new(test, e))
|
34
|
+
rescue Exception => e
|
35
|
+
@report.on_error(ErroredTest.new(test, e))
|
36
|
+
raise if running_global_setup_or_teardown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|