mcmire-protest 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|