protest 0.3.0 → 0.3.1

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.
@@ -2,16 +2,16 @@
2
2
 
3
3
  require "protest"
4
4
 
5
- Protest.context("A user") do
5
+ Protest.describe("A user") do
6
6
  setup do
7
7
  @user = User.new(:name => "John Doe", :email => "john@example.org")
8
8
  end
9
9
 
10
- test "has a name" do
10
+ it "has a name" do
11
11
  assert_equal "John Doe", @user.name
12
12
  end
13
13
 
14
- test "has an email" do
14
+ it "has an email" do
15
15
  assert_equal "john@example.org", @user.email
16
16
  end
17
17
  end
@@ -49,7 +49,7 @@ If you need to run code before or after each test, declare a +setup+ or
49
49
 
50
50
  +setup+ and +teardown+ blocks are evaluated in the same context as your test,
51
51
  which means any instance variables defined in any of them are available in the
52
- rest.
52
+ rest. Both methods are aliased for your comfort as before and after respectively.
53
53
 
54
54
  You can also use +global_setup+ and +global_teardown+ to run code only once per
55
55
  test case. +global_setup+ blocks will run once before the first test is run, and
@@ -69,13 +69,13 @@ them.
69
69
 
70
70
  Break down your test into logical chunks with nested contexts:
71
71
 
72
- Protest.context("A user") do
72
+ Protest.describe("A user") do
73
73
  setup do
74
74
  @user = User.make
75
75
  end
76
76
 
77
77
  context "when validating" do
78
- test "validates name" do
78
+ it "validates name" do
79
79
  @user.name = nil
80
80
  assert !@user.valid?
81
81
  end
@@ -84,7 +84,7 @@ Break down your test into logical chunks with nested contexts:
84
84
  end
85
85
 
86
86
  context "doing something else" do
87
- # your get the idea
87
+ # you get the idea
88
88
  end
89
89
  end
90
90
 
@@ -187,6 +187,34 @@ Will output, when run with the <tt>:documentation</tt> report:
187
187
 
188
188
  This is similar to the specdoc runner in rspec[http://rspec.info].
189
189
 
190
+ === Summary report
191
+
192
+ Use this report by calling <tt>Protest.report_with(:summary)</tt>
193
+
194
+ Will output a brief summary with the total number of tests, assertions, passed
195
+ tests, pending tests, failed tests and errors.
196
+
197
+ === Stories report
198
+
199
+ Use this report by calling <tt>Protest.report_with(:stories)</tt>
200
+
201
+ This report is based on Citrusbyte's Stories[http://github.com/citrusbyte/stories],
202
+ by Damian Janowski and Michel Martens.
203
+
204
+ === Turn report
205
+
206
+ Use this report by calling <tt>Protest.report_with(:turn)</tt>
207
+
208
+ This report displays each test on a separate line with failures being displayed
209
+ immediately instead of at the end of the tests.
210
+
211
+ You might find this useful when running a large test suite, as it can be very
212
+ frustrating to see a failure (....F...) and then have to wait until all the
213
+ tests finish before you can see what the exact failure was.
214
+
215
+ This report is based on the output displayed by TURN[http://github.com/TwP/turn],
216
+ Test::Unit Reporter (New) by Tim Pease.
217
+
190
218
  === Defining your own reports
191
219
 
192
220
  This is really, really easy. All you need to do is subclass Report, and
@@ -196,5 +224,6 @@ Protest::Reports::Progress and Protest::Reports::Documentation.
196
224
 
197
225
  == Legal
198
226
 
227
+ Maintainer:: Matías Flores — http://matflores.com
199
228
  Author:: Nicolás Sanguinetti — http://nicolassanguinetti.info
200
229
  License:: MIT (see bundled LICENSE file for more info)
@@ -98,6 +98,9 @@ require "protest/report"
98
98
  require "protest/reports"
99
99
  require "protest/reports/progress"
100
100
  require "protest/reports/documentation"
101
+ require "protest/reports/turn"
102
+ require "protest/reports/summary"
103
+ require "protest/reports/stories"
101
104
 
102
105
  Protest.autorun = true
103
106
  Protest.report_with(:progress)
@@ -0,0 +1,61 @@
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
@@ -0,0 +1,72 @@
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
@@ -0,0 +1,22 @@
1
+ module Protest
2
+ # The +:summary+ report will output a brief summary with the total number
3
+ # of tests, assertions, passed tests, pending tests, failed tests and
4
+ # errors.
5
+ class Reports::Summary < Report
6
+ include Utils::Summaries
7
+ include Utils::ColorfulOutput
8
+
9
+ attr_reader :stream #:nodoc:
10
+
11
+ # Set the stream where the report will be written to. STDOUT by default.
12
+ def initialize(stream=STDOUT)
13
+ @stream = stream
14
+ end
15
+
16
+ on :end do |report|
17
+ report.summarize_test_totals
18
+ end
19
+ end
20
+
21
+ add_report :summary, Reports::Summary
22
+ end
@@ -0,0 +1,76 @@
1
+ module Protest
2
+ # This report displays each test on a separate line with failures being
3
+ # displayed immediately instead of at the end of the tests.
4
+ #
5
+ # You might find this useful when running a large test suite, as it can
6
+ # be very frustrating to see a failure (....F...) and then have to wait
7
+ # until all the tests finish before you can see what the exact failure
8
+ # was.
9
+ #
10
+ # This report is based on the output displayed by TURN[http://github.com/TwP/turn],
11
+ # Test::Unit Reporter (New) by Tim Pease.
12
+ class Reports::Turn < Report
13
+ include Utils::Summaries
14
+ include Utils::ColorfulOutput
15
+
16
+ attr_reader :stream #:nodoc:
17
+
18
+ PASS = "PASS"
19
+ FAIL = "FAIL"
20
+ ERROR = "ERROR"
21
+ PENDING = "PENDING"
22
+
23
+ # Set the stream where the report will be written to. STDOUT by default.
24
+ def initialize(stream=STDOUT)
25
+ @stream = stream
26
+ end
27
+
28
+ on :enter do |report, context|
29
+ report.puts context.description unless context.tests.empty?
30
+ end
31
+
32
+ on :test do |report, test|
33
+ report.print " %-67s" % test.test_name
34
+ end
35
+
36
+ on :pass do |report, passed_test|
37
+ report.puts PASS, :passed
38
+ end
39
+
40
+ on :failure do |report, failed_test|
41
+ report.puts FAIL, :failed
42
+
43
+ report.puts "\t#{failed_test.error_message}", :failed
44
+ failed_test.backtrace.each { |backtrace| report.puts "\t#{backtrace}", :failed }
45
+ end
46
+
47
+ on :error do |report, errored_test|
48
+ report.puts ERROR, :errored
49
+
50
+ report.puts "\t#{errored_test.error_message}", :errored
51
+ errored_test.backtrace.each { |backtrace| report.puts "\t#{backtrace}", :failed }
52
+ end
53
+
54
+ on :pending do |report, pending_test|
55
+ report.puts PENDING, :pending
56
+ end
57
+
58
+ on :end do |report|
59
+ pass = report.passes.count
60
+ pending = report.pendings.count
61
+ failure = report.failures.count
62
+ error = report.errors.count
63
+ total = report.tests.count
64
+
65
+ bar = '=' * 78
66
+ colorize_as = pass == total ? :passed : (pass + pending == total ? :pending : :failed)
67
+
68
+ report.puts bar, colorize_as
69
+ report.puts " pass: %d, pending: %d, fail: %d, error: %d" % [pass, pending, failure, error]
70
+ report.puts " total: %d tests with %d assertions in #{report.time_elapsed} seconds" % [total, report.assertions]
71
+ report.puts bar, colorize_as
72
+ end
73
+ end
74
+
75
+ add_report :turn, Reports::Turn
76
+ end
@@ -10,30 +10,35 @@ module Protest
10
10
  # events on the runner's report, at the +start+ and +end+ of the test run,
11
11
  # and before and after each test case (+enter+ and +exit+.)
12
12
  def run(*test_cases)
13
- @report.on_start if @report.respond_to?(:on_start)
13
+ fire_event :start
14
14
  test_cases.each do |test_case|
15
- @report.on_enter(test_case) if @report.respond_to?(:on_enter)
15
+ fire_event :enter, test_case
16
16
  test_case.run(self)
17
- @report.on_exit(test_case) if @report.respond_to?(:on_exit)
17
+ fire_event :exit, test_case
18
18
  end
19
- @report.on_end if @report.respond_to?(:on_end)
19
+ fire_event :end
20
20
  end
21
21
 
22
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
23
+ # of the test as an argument.
24
+ def report(test)
25
+ fire_event(:test, Test.new(test)) if test.real?
28
26
  test.run(@report)
29
- @report.on_pass(PassedTest.new(test)) unless running_global_setup_or_teardown
27
+ fire_event(:pass, PassedTest.new(test)) if test.real?
30
28
  rescue Pending => e
31
- @report.on_pending(PendingTest.new(test, e))
29
+ fire_event :pending, PendingTest.new(test, e)
32
30
  rescue AssertionFailed => e
33
- @report.on_failure(FailedTest.new(test, e))
31
+ fire_event :failure, FailedTest.new(test, e)
34
32
  rescue Exception => e
35
- @report.on_error(ErroredTest.new(test, e))
36
- raise if running_global_setup_or_teardown
33
+ fire_event :error, ErroredTest.new(test, e)
34
+ raise if test.raise_exceptions?
35
+ end
36
+
37
+ protected
38
+
39
+ def fire_event(event, *args)
40
+ event_handler_method = :"on_#{event}"
41
+ @report.send(event_handler_method, *args) if @report.respond_to?(event_handler_method)
37
42
  end
38
43
  end
39
44
  end
@@ -0,0 +1,128 @@
1
+ module Protest
2
+ def self.story(description, &block)
3
+ context(description) do
4
+ Protest::Stories.all[self] = Protest::Stories::Story.new(description)
5
+ class_eval(&block) if block_given?
6
+ end
7
+ end
8
+
9
+ def self.scenario(name, &block)
10
+ scenario = Protest::Stories::Scenario.new(name)
11
+
12
+ Protest::Stories.all[self].scenarios << scenario
13
+
14
+ test(name) do
15
+ @scenario = scenario
16
+ instance_eval(&block)
17
+ end
18
+ end
19
+
20
+ module Stories
21
+ def self.all
22
+ @all ||= {}
23
+ end
24
+
25
+ module TestCase
26
+ def self.included(base)
27
+ class << base
28
+ def story(description, &block)
29
+ context(description) do
30
+ Protest::Stories.all[self] = Protest::Stories::Story.new(description)
31
+ class_eval(&block) if block_given?
32
+ end
33
+ end
34
+
35
+ def scenario(name, &block)
36
+ scenario = Protest::Stories::Scenario.new(name)
37
+
38
+ Protest::Stories.all[self].scenarios << scenario
39
+
40
+ test(name) do
41
+ @scenario = scenario
42
+ instance_eval(&block)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ class Story
50
+ attr_accessor :name, :scenarios
51
+
52
+ def initialize(name)
53
+ @name = name
54
+ @scenarios = []
55
+ end
56
+ end
57
+
58
+ class Scenario
59
+ attr_accessor :name, :steps, :assertions
60
+
61
+ def initialize(name)
62
+ @name = name
63
+ @steps = []
64
+ @assertions = []
65
+ end
66
+ end
67
+
68
+ module Methods
69
+ def report(text, &block)
70
+ @scenario.steps << text
71
+ silent(&block) if block_given?
72
+ end
73
+
74
+ def silent(&block)
75
+ scenario, @scenario = @scenario, Stories::Scenario.new("#{@scenario.name} (Silent)")
76
+
77
+ begin
78
+ block.call
79
+ ensure
80
+ @scenario = scenario
81
+ end
82
+ end
83
+ end
84
+
85
+ module Webrat
86
+ def report_for(action, &block)
87
+ define_method(action) do |*args|
88
+ @scenario.steps << block.call(*args)
89
+ super(*args)
90
+ end
91
+ end
92
+ module_function :report_for
93
+
94
+ report_for :click_link do |name|
95
+ "Click #{quote(name)}"
96
+ end
97
+
98
+ report_for :click_button do |name|
99
+ "Click #{quote(name)}"
100
+ end
101
+
102
+ report_for :fill_in do |name, opts|
103
+ "Fill in #{quote(name)} with #{quote(opts[:with])}"
104
+ end
105
+
106
+ report_for :visit do |page|
107
+ "Go to #{quote(page)}"
108
+ end
109
+
110
+ report_for :check do |name|
111
+ "Check #{quote(name)}"
112
+ end
113
+
114
+ report_for :assert_contain do |text|
115
+ "I should see #{quote(text)}"
116
+ end
117
+
118
+ def quote(text)
119
+ "“#{text}”"
120
+ end
121
+ module_function :quote
122
+ end
123
+ end
124
+
125
+ Protest::TestCase.send(:include, Protest::Stories::TestCase)
126
+ Protest::TestCase.send(:include, Protest::Stories::Methods)
127
+ Protest::TestCase.send(:include, Protest::Stories::Webrat)
128
+ end
@@ -34,9 +34,9 @@ module Protest
34
34
  # Run all tests in this context. Takes a Runner instance in order to
35
35
  # provide output.
36
36
  def self.run(runner)
37
- runner.report(TestWrapper.new(:setup, self), true)
38
- tests.each {|test| runner.report(test, false) }
39
- runner.report(TestWrapper.new(:teardown, self), true)
37
+ runner.report(TestWrapper.new(:setup, self))
38
+ tests.each {|test| runner.report(test) }
39
+ runner.report(TestWrapper.new(:teardown, self))
40
40
  rescue Exception => e
41
41
  # If any exception bubbles up here, then it means it was during the
42
42
  # global setup/teardown blocks, so let's just skip the rest of this
@@ -199,6 +199,16 @@ module Protest
199
199
  @name
200
200
  end
201
201
 
202
+ # Tests must not re-raise exceptions
203
+ def raise_exceptions?
204
+ false
205
+ end
206
+
207
+ # This is a real test
208
+ def real?
209
+ true
210
+ end
211
+
202
212
  private
203
213
 
204
214
  def setup #:nodoc:
@@ -248,6 +258,15 @@ module Protest
248
258
  def run(report)
249
259
  @test.send("do_global_#{@type}")
250
260
  end
261
+
262
+ def raise_exceptions?
263
+ true
264
+ end
265
+
266
+ # This is not a real test but a fake one
267
+ def real?
268
+ false
269
+ end
251
270
  end
252
271
  end
253
272
  end
@@ -1,14 +1,14 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "protest"
3
- s.version = "0.3.0"
4
- s.date = "2010-03-11"
3
+ s.version = "0.3.1"
4
+ s.date = "2010-05-19"
5
5
 
6
6
  s.description = "Protest is a tiny, simple, and easy-to-extend test framework"
7
7
  s.summary = s.description
8
- s.homepage = "http://rubyprotest.org"
8
+ s.homepage = "http://matflores.github.com/protest"
9
9
 
10
- s.authors = ["Nicolás Sanguinetti"]
11
- s.email = "contacto@nicolassanguinetti.info"
10
+ s.authors = ["Nicolás Sanguinetti", "Matías Flores"]
11
+ s.email = "mflores@atlanware.com"
12
12
 
13
13
  s.require_paths = ["lib"]
14
14
  s.rubyforge_project = "protest"
@@ -29,10 +29,15 @@ lib/protest/utils/colorful_output.rb
29
29
  lib/protest/test_case.rb
30
30
  lib/protest/tests.rb
31
31
  lib/protest/runner.rb
32
+ lib/protest/stories.rb
32
33
  lib/protest/report.rb
33
34
  lib/protest/reports.rb
34
35
  lib/protest/reports/progress.rb
35
36
  lib/protest/reports/documentation.rb
37
+ lib/protest/reports/summary.rb
38
+ lib/protest/reports/turn.rb
39
+ lib/protest/reports/stories.rb
40
+ lib/protest/reports/stories/pdf.rb
36
41
  lib/protest/rails.rb
37
42
  ]
38
43
  end
metadata CHANGED
@@ -5,21 +5,22 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 0
9
- version: 0.3.0
8
+ - 1
9
+ version: 0.3.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - "Nicol\xC3\xA1s Sanguinetti"
13
+ - "Mat\xC3\xADas Flores"
13
14
  autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-03-11 00:00:00 -03:00
18
+ date: 2010-05-19 00:00:00 -03:00
18
19
  default_executable:
19
20
  dependencies: []
20
21
 
21
22
  description: Protest is a tiny, simple, and easy-to-extend test framework
22
- email: contacto@nicolassanguinetti.info
23
+ email: mflores@atlanware.com
23
24
  executables: []
24
25
 
25
26
  extensions: []
@@ -40,13 +41,18 @@ files:
40
41
  - lib/protest/test_case.rb
41
42
  - lib/protest/tests.rb
42
43
  - lib/protest/runner.rb
44
+ - lib/protest/stories.rb
43
45
  - lib/protest/report.rb
44
46
  - lib/protest/reports.rb
45
47
  - lib/protest/reports/progress.rb
46
48
  - lib/protest/reports/documentation.rb
49
+ - lib/protest/reports/summary.rb
50
+ - lib/protest/reports/turn.rb
51
+ - lib/protest/reports/stories.rb
52
+ - lib/protest/reports/stories/pdf.rb
47
53
  - lib/protest/rails.rb
48
54
  has_rdoc: true
49
- homepage: http://rubyprotest.org
55
+ homepage: http://matflores.github.com/protest
50
56
  licenses: []
51
57
 
52
58
  post_install_message: