protest 0.4.2 → 0.5.0
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/CHANGELOG.md +12 -0
- data/LICENSE +1 -1
- data/README.md +256 -0
- data/Rakefile +0 -19
- data/lib/protest.rb +10 -20
- data/lib/protest/report.rb +10 -0
- data/lib/protest/reports/documentation.rb +0 -10
- data/lib/protest/reports/progress.rb +0 -10
- data/lib/protest/reports/summary.rb +0 -10
- data/lib/protest/reports/turn.rb +0 -10
- data/lib/protest/runner.rb +2 -3
- data/lib/protest/test_case.rb +32 -117
- data/lib/protest/utils/summaries.rb +2 -2
- data/lib/protest/version.rb +3 -0
- data/protest.gemspec +13 -39
- metadata +40 -61
- data/README.rdoc +0 -237
- data/lib/protest/reports/stories.rb +0 -61
- data/lib/protest/reports/stories/pdf.rb +0 -72
- data/lib/protest/stories.rb +0 -130
data/README.rdoc
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
= Protest, the simplicity rebel test framework
|
2
|
-
|
3
|
-
require "protest"
|
4
|
-
|
5
|
-
Protest.describe("A user") do
|
6
|
-
setup do
|
7
|
-
@user = User.new(:name => "John Doe", :email => "john@example.org")
|
8
|
-
end
|
9
|
-
|
10
|
-
it "has a name" do
|
11
|
-
assert_equal "John Doe", @user.name
|
12
|
-
end
|
13
|
-
|
14
|
-
it "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/matflores/protest.git v0.4.0
|
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. Both methods are aliased for your comfort as before and after respectively.
|
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.describe("A user") do
|
73
|
-
setup do
|
74
|
-
@user = User.make
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when validating" do
|
78
|
-
it "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
|
-
# you 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
|
-
Previous versions of Protest used to bundle all the assertions defined in
|
121
|
-
Test::Unit, but that has changed and now Protest includes just three basic
|
122
|
-
assertion methods:
|
123
|
-
|
124
|
-
- assert
|
125
|
-
- assert_equal
|
126
|
-
- assert_raise
|
127
|
-
|
128
|
-
If you want to add assertions, just define methods that rely on +assert+.
|
129
|
-
This method takes a boolean and an optional error message as arguments, and
|
130
|
-
the assertion is considered to fail if the boolean evaluates to neither
|
131
|
-
+false+ nor +nil+.
|
132
|
-
|
133
|
-
For example:
|
134
|
-
|
135
|
-
module AwesomenessAssertions
|
136
|
-
def assert_awesomeness(object)
|
137
|
-
assert object.awesome?, "#{object.inspect} is not awesome enough"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
class Protest::TestCase
|
142
|
-
include AwesomenessAssertions
|
143
|
-
end
|
144
|
-
|
145
|
-
You could also define rspec-like matchers if you like that style. See
|
146
|
-
<tt>matchers.rb</tt> in the examples directory for an example.
|
147
|
-
|
148
|
-
== Reports
|
149
|
-
|
150
|
-
Protest can report the output of a test suite in many ways. The library ships
|
151
|
-
with a <tt>:progress</tt> report, and a <tt>:documentation</tt> report,
|
152
|
-
<tt>:progress</tt> being the default.
|
153
|
-
|
154
|
-
=== Progress report
|
155
|
-
|
156
|
-
This is the default option, but you can force this by calling
|
157
|
-
<tt>Protest.report_with(:progress)</tt>.
|
158
|
-
|
159
|
-
The progress report will output the "classic" Test::Unit output of periods for
|
160
|
-
passing tests, "F" for failing assertions, "E" for unrescued exceptions, and
|
161
|
-
"P" for pending tests, in full color.
|
162
|
-
|
163
|
-
=== Documentation report
|
164
|
-
|
165
|
-
Use this report by calling <tt>Protest.report_with(:documentation)</tt>
|
166
|
-
|
167
|
-
For each testcase in your suite, this will output the description of the test
|
168
|
-
case (whatever you provide TestCase.context), followed by the name of each test
|
169
|
-
in that context, one per line. For example:
|
170
|
-
|
171
|
-
Protest.context "A user" do
|
172
|
-
test "has a name"
|
173
|
-
test "has an email"
|
174
|
-
|
175
|
-
context "validations" do
|
176
|
-
test "ensure the email can't be blank"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
Will output, when run with the <tt>:documentation</tt> report:
|
181
|
-
|
182
|
-
A user
|
183
|
-
- has a name (Not Yet Implemented)
|
184
|
-
- has an email (Not Yet Implemented)
|
185
|
-
|
186
|
-
A user validations
|
187
|
-
- ensure the email can't be blank (Not Yet Implemented)
|
188
|
-
|
189
|
-
(The 'Not Yet Implemented' messages are because the tests have no body. See
|
190
|
-
"Pending tests", above.)
|
191
|
-
|
192
|
-
This is similar to the specdoc runner in rspec[http://rspec.info].
|
193
|
-
|
194
|
-
=== Summary report
|
195
|
-
|
196
|
-
Use this report by calling <tt>Protest.report_with(:summary)</tt>
|
197
|
-
|
198
|
-
Will output a brief summary with the total number of tests, assertions, passed
|
199
|
-
tests, pending tests, failed tests and errors.
|
200
|
-
|
201
|
-
=== Stories report
|
202
|
-
|
203
|
-
Use this report by calling <tt>Protest.report_with(:stories)</tt>
|
204
|
-
|
205
|
-
This report is based on Citrusbyte's Stories[http://github.com/citrusbyte/stories],
|
206
|
-
by Damian Janowski and Michel Martens.
|
207
|
-
|
208
|
-
=== Turn report
|
209
|
-
|
210
|
-
Use this report by calling <tt>Protest.report_with(:turn)</tt>
|
211
|
-
|
212
|
-
This report displays each test on a separate line with failures being displayed
|
213
|
-
immediately instead of at the end of the tests.
|
214
|
-
|
215
|
-
You might find this useful when running a large test suite, as it can be very
|
216
|
-
frustrating to see a failure (....F...) and then have to wait until all the
|
217
|
-
tests finish before you can see what the exact failure was.
|
218
|
-
|
219
|
-
This report is based on the output displayed by TURN[http://github.com/TwP/turn],
|
220
|
-
Test::Unit Reporter (New) by Tim Pease.
|
221
|
-
|
222
|
-
=== Defining your own reports
|
223
|
-
|
224
|
-
This is really, really easy. All you need to do is subclass Report, and
|
225
|
-
register your subclass by calling +Protest.add_report+. See the
|
226
|
-
documentation for details, or take a look at the source code for
|
227
|
-
Protest::Reports::Progress and Protest::Reports::Documentation.
|
228
|
-
|
229
|
-
== Using Rails?
|
230
|
-
|
231
|
-
If you are using Rails you may want to take a look at http://github.com/matflores/protest-rails.
|
232
|
-
|
233
|
-
== Legal
|
234
|
-
|
235
|
-
Maintainer:: Matías Flores — http://matflores.com
|
236
|
-
Author:: Nicolás Sanguinetti — http://nicolassanguinetti.info
|
237
|
-
License:: MIT (see bundled LICENSE file for more info)
|
@@ -1,61 +0,0 @@
|
|
1
|
-
module Protest
|
2
|
-
# This report is based on Citrusbyte's Stories[http://github.com/citrusbyte/stories],
|
3
|
-
# by Damian Janowski and Michel Martens.
|
4
|
-
class Reports::Stories < Report
|
5
|
-
include Utils::Summaries
|
6
|
-
include Utils::ColorfulOutput
|
7
|
-
|
8
|
-
attr_reader :stream #:nodoc:
|
9
|
-
|
10
|
-
# Set the stream where the report will be written to. STDOUT by default.
|
11
|
-
def initialize(stream=STDOUT)
|
12
|
-
@stream = stream
|
13
|
-
end
|
14
|
-
|
15
|
-
on :pass do |report, pass|
|
16
|
-
report.print ".", :passed
|
17
|
-
end
|
18
|
-
|
19
|
-
on :pending do |report, pending|
|
20
|
-
report.print "P", :pending
|
21
|
-
end
|
22
|
-
|
23
|
-
on :failure do |report, failure|
|
24
|
-
report.print "F", :failed
|
25
|
-
end
|
26
|
-
|
27
|
-
on :error do |report, error|
|
28
|
-
report.print "E", :errored
|
29
|
-
end
|
30
|
-
|
31
|
-
on :end do |report|
|
32
|
-
report.puts
|
33
|
-
|
34
|
-
Stories.all.values.to_a.each_with_index do |story,i|
|
35
|
-
report.puts "- #{story.name}"
|
36
|
-
|
37
|
-
story.scenarios.each do |scenario|
|
38
|
-
report.puts " #{scenario.name}"
|
39
|
-
|
40
|
-
unless scenario.steps.empty? && scenario.assertions.empty?
|
41
|
-
scenario.steps.each do |step|
|
42
|
-
report.puts " #{step}"
|
43
|
-
end
|
44
|
-
|
45
|
-
scenario.assertions.each do |assertion|
|
46
|
-
report.puts " #{assertion}"
|
47
|
-
end
|
48
|
-
|
49
|
-
report.puts
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
report.puts unless i + 1 == Stories.all.values.size
|
54
|
-
end
|
55
|
-
|
56
|
-
report.puts "%d stories, %d scenarios" % [Stories.all.values.size, Stories.all.values.inject(0) {|total,s| total + s.scenarios.size }]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
add_report :stories, Reports::Stories
|
61
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
|
3
|
-
gem "prawn", "~> 0.4"
|
4
|
-
require "prawn"
|
5
|
-
|
6
|
-
module Protest
|
7
|
-
# This report is based on Citrusbyte's Stories[http://github.com/citrusbyte/stories],
|
8
|
-
# by Damian Janowski and Michel Martens.
|
9
|
-
module Reports
|
10
|
-
class Stories::PDF < Report
|
11
|
-
include Utils::Summaries
|
12
|
-
include Utils::ColorfulOutput
|
13
|
-
|
14
|
-
attr_reader :stream #:nodoc:
|
15
|
-
|
16
|
-
# Set the stream where the report will be written to. STDOUT by default.
|
17
|
-
def initialize(stream=STDOUT)
|
18
|
-
@stream = stream
|
19
|
-
end
|
20
|
-
|
21
|
-
def render_header(pdf)
|
22
|
-
end
|
23
|
-
|
24
|
-
def render_many(pdf, elements)
|
25
|
-
elements.each do |el|
|
26
|
-
pdf.text el.to_s
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
on :end do |report|
|
31
|
-
Prawn::Document.generate("stories.pdf", :page_size => "A4") do |pdf|
|
32
|
-
report.render_header(pdf)
|
33
|
-
|
34
|
-
pdf.text "User Acceptance Tests", :size => 20, :style => :bold
|
35
|
-
|
36
|
-
pdf.move_down(15)
|
37
|
-
|
38
|
-
Protest::Stories.all.values.each do |story|
|
39
|
-
pdf.text story.name, :style => :bold
|
40
|
-
|
41
|
-
story.scenarios.each_with_index do |scenario,i|
|
42
|
-
scenario_leading = 15
|
43
|
-
|
44
|
-
pdf.span(pdf.bounds.width - scenario_leading, :position => scenario_leading) do
|
45
|
-
pdf.text "— #{scenario.name}"
|
46
|
-
|
47
|
-
pdf.fill_color "666666"
|
48
|
-
|
49
|
-
unless scenario.steps.empty? && scenario.assertions.empty?
|
50
|
-
pdf.span(pdf.bounds.width - 30, :position => 30) do
|
51
|
-
pdf.font_size(9) do
|
52
|
-
report.render_many(pdf, scenario.steps)
|
53
|
-
report.render_many(pdf, scenario.assertions)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
pdf.move_down(5) unless i + 1 == story.scenarios.size
|
59
|
-
|
60
|
-
pdf.fill_color "000000"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
pdf.move_down(10)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
add_report :stories_pdf, Reports::Stories::PDF
|
72
|
-
end
|
data/lib/protest/stories.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Protest
|
4
|
-
def self.story(description, &block)
|
5
|
-
context(description) do
|
6
|
-
Protest::Stories.all[self] = Protest::Stories::Story.new(description)
|
7
|
-
class_eval(&block) if block_given?
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.scenario(name, &block)
|
12
|
-
scenario = Protest::Stories::Scenario.new(name)
|
13
|
-
|
14
|
-
Protest::Stories.all[self].scenarios << scenario
|
15
|
-
|
16
|
-
test(name) do
|
17
|
-
@scenario = scenario
|
18
|
-
instance_eval(&block)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module Stories
|
23
|
-
def self.all
|
24
|
-
@all ||= {}
|
25
|
-
end
|
26
|
-
|
27
|
-
module TestCase
|
28
|
-
def self.included(base)
|
29
|
-
class << base
|
30
|
-
def story(description, &block)
|
31
|
-
context(description) do
|
32
|
-
Protest::Stories.all[self] = Protest::Stories::Story.new(description)
|
33
|
-
class_eval(&block) if block_given?
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def scenario(name, &block)
|
38
|
-
scenario = Protest::Stories::Scenario.new(name)
|
39
|
-
|
40
|
-
Protest::Stories.all[self].scenarios << scenario
|
41
|
-
|
42
|
-
test(name) do
|
43
|
-
@scenario = scenario
|
44
|
-
instance_eval(&block)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
class Story
|
52
|
-
attr_accessor :name, :scenarios
|
53
|
-
|
54
|
-
def initialize(name)
|
55
|
-
@name = name
|
56
|
-
@scenarios = []
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class Scenario
|
61
|
-
attr_accessor :name, :steps, :assertions
|
62
|
-
|
63
|
-
def initialize(name)
|
64
|
-
@name = name
|
65
|
-
@steps = []
|
66
|
-
@assertions = []
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
module Methods
|
71
|
-
def report(text, &block)
|
72
|
-
@scenario.steps << text
|
73
|
-
silent(&block) if block_given?
|
74
|
-
end
|
75
|
-
|
76
|
-
def silent(&block)
|
77
|
-
scenario, @scenario = @scenario, Stories::Scenario.new("#{@scenario.name} (Silent)")
|
78
|
-
|
79
|
-
begin
|
80
|
-
block.call
|
81
|
-
ensure
|
82
|
-
@scenario = scenario
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
module Webrat
|
88
|
-
def report_for(action, &block)
|
89
|
-
define_method(action) do |*args|
|
90
|
-
@scenario.steps << block.call(*args)
|
91
|
-
super(*args)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
module_function :report_for
|
95
|
-
|
96
|
-
report_for :click_link do |name|
|
97
|
-
"Click #{quote(name)}"
|
98
|
-
end
|
99
|
-
|
100
|
-
report_for :click_button do |name|
|
101
|
-
"Click #{quote(name)}"
|
102
|
-
end
|
103
|
-
|
104
|
-
report_for :fill_in do |name, opts|
|
105
|
-
"Fill in #{quote(name)} with #{quote(opts[:with])}"
|
106
|
-
end
|
107
|
-
|
108
|
-
report_for :visit do |page|
|
109
|
-
"Go to #{quote(page)}"
|
110
|
-
end
|
111
|
-
|
112
|
-
report_for :check do |name|
|
113
|
-
"Check #{quote(name)}"
|
114
|
-
end
|
115
|
-
|
116
|
-
report_for :assert_contain do |text|
|
117
|
-
"I should see #{quote(text)}"
|
118
|
-
end
|
119
|
-
|
120
|
-
def quote(text)
|
121
|
-
"“#{text}”"
|
122
|
-
end
|
123
|
-
module_function :quote
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
Protest::TestCase.send(:include, Protest::Stories::TestCase)
|
128
|
-
Protest::TestCase.send(:include, Protest::Stories::Methods)
|
129
|
-
Protest::TestCase.send(:include, Protest::Stories::Webrat)
|
130
|
-
end
|