protest 0.3.0 → 0.3.1

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