thumblemonks-protest 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +9 -8
- data/Rakefile +5 -0
- data/lib/protest.rb +3 -1
- data/lib/protest/assertion.rb +15 -17
- data/lib/protest/context.rb +26 -14
- data/lib/protest/macros.rb +6 -6
- data/lib/protest/report.rb +3 -4
- data/protest.gemspec +3 -2
- data/test/assertion_test.rb +2 -2
- data/test/benchmark/simple_context_and_assertions.rb +76 -0
- data/test/context_test.rb +13 -6
- metadata +4 -4
data/README.markdown
CHANGED
@@ -43,7 +43,7 @@ For example:
|
|
43
43
|
|
44
44
|
Here, you should begin to notice that tests themselves return the actual value. You do not write assertions into the test. Assertions are "aspected" onto the test. If the test above did not return 'foo@bar.com' for `@user.email`, the assertion would have failed.
|
45
45
|
|
46
|
-
The `equals` modifier works with any type of value, including nil's. However, if you wanted to test for nil explicitly, you could
|
46
|
+
The `equals` modifier works with any type of value, including nil's. However, if you wanted to test for nil explicitly, you could simply do this:
|
47
47
|
|
48
48
|
context "a new user" do
|
49
49
|
setup { @user = User.new }
|
@@ -91,7 +91,7 @@ Sometimes, your test raises an exception that you actually expected.
|
|
91
91
|
|
92
92
|
context "a new user" do
|
93
93
|
setup { @user = User.new }
|
94
|
-
asserts("
|
94
|
+
asserts("invalid data") { @user.save! }.raises(ActiveRecord::RecordInvalid)
|
95
95
|
end
|
96
96
|
|
97
97
|
#### Example: Kind Of
|
@@ -100,7 +100,7 @@ When you want to test that an expression returns an object of an expected type:
|
|
100
100
|
|
101
101
|
context "a new user" do
|
102
102
|
setup { @user = User.new }
|
103
|
-
asserts("
|
103
|
+
asserts("its balance") { @user.balance }.kind_of(Currency)
|
104
104
|
end
|
105
105
|
|
106
106
|
#### Example: Nested contexts
|
@@ -224,7 +224,7 @@ Make sure to check out the Protest + Sinatra testing macros in Chicago.
|
|
224
224
|
|
225
225
|
### With Rails
|
226
226
|
|
227
|
-
It's doubtful that Protest works with Rails very easily as Protest completely replaces Test::Unit. I haven't tried it yet, but intend to with my next new Rails project. Porting would probably take some time unless you only have a few test cases.
|
227
|
+
It's doubtful that Protest works with Rails very easily as Protest completely replaces Test::Unit. I haven't tried it yet, but intend to with my next new Rails project. Porting would probably take some time unless you only have a few test cases. Porting is made somewhat easier if you're already using Shoulda; you can replace the `TestCase` definition with a `context` of the same name as the class under test I suppose.
|
228
228
|
|
229
229
|
## Extending Protest with Macros
|
230
230
|
|
@@ -281,13 +281,14 @@ Notice in the new macro we defined the use of the magical **actual** variable. `
|
|
281
281
|
|
282
282
|
TONS OF STUFF
|
283
283
|
|
284
|
-
1. Documentation
|
284
|
+
1. Documentation about the `topic` method
|
285
|
+
1. Better documentation for everything
|
285
286
|
1. Refactor reporting; some abstracting is needed for recording a result (for instance)
|
286
|
-
1. Need to know where in backtrace a test failed (line number, etc.)
|
287
|
+
1. Need to know where in backtrace a test failed (line number, etc.); i.e. backtrace filtering for clarity
|
287
288
|
1. More assertion macros: throws, etc.
|
288
289
|
1. Aliases for context "with, without, when, ..."; add those words to test description
|
289
290
|
1. Optimization and simplification (ex. flog is complaining print\_result\_stack)
|
290
|
-
1. Better error messages
|
291
|
+
1. Better error messages?
|
291
292
|
1. Perhaps: Multiple setup blocks in one context
|
292
293
|
1. Perhaps: association macro chaining
|
293
|
-
1. Perhaps: Uhhhhh ... a teardown method (maybe :)
|
294
|
+
1. Perhaps: Uhhhhh ... a teardown method (only maybe. not sold :)
|
data/Rakefile
CHANGED
@@ -15,6 +15,11 @@ task :flog do
|
|
15
15
|
puts %x[find ./lib -name *.rb | xargs flog]
|
16
16
|
end
|
17
17
|
|
18
|
+
desc "Run Flay against library (except tests)"
|
19
|
+
task :flay do
|
20
|
+
puts %x[find ./lib -name *.rb | xargs flay]
|
21
|
+
end
|
22
|
+
|
18
23
|
desc "Run Roodi against library (except tests)"
|
19
24
|
task :roodi do
|
20
25
|
puts %x[find ./lib -name *.rb | xargs roodi]
|
data/lib/protest.rb
CHANGED
@@ -29,9 +29,11 @@ module Protest
|
|
29
29
|
|
30
30
|
#
|
31
31
|
# Reporter
|
32
|
-
|
32
|
+
|
33
33
|
def self.reporter; @reporter ||= TextReport.new; end
|
34
34
|
def self.reporter=(report); @reporter = report; end
|
35
|
+
def self.silently!; @silently = true; end
|
36
|
+
def self.silently?; @silently || false; end
|
35
37
|
|
36
38
|
#
|
37
39
|
# Exception
|
data/lib/protest/assertion.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Protest
|
2
2
|
|
3
3
|
class Assertion
|
4
|
-
attr_reader :raised, :to_s
|
5
|
-
def initialize(description,
|
4
|
+
attr_reader :raised, :to_s, :description
|
5
|
+
def initialize(description, situation, &block)
|
6
6
|
@description = @to_s = description
|
7
|
-
actualize(
|
7
|
+
actualize(situation, &block)
|
8
8
|
end
|
9
9
|
|
10
10
|
def actual
|
@@ -12,26 +12,24 @@ module Protest
|
|
12
12
|
@actual
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
rescue Failure => e
|
18
|
-
@failure = e # Smelly (for now)
|
15
|
+
def fail(message)
|
16
|
+
@failure = Failure.new(message, self) unless errored?
|
19
17
|
end
|
20
18
|
|
21
|
-
def
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def failure?; @failure; end
|
26
|
-
def error?; !failure? && raised; end
|
27
|
-
def passed?; !failure? && !error?; end
|
19
|
+
def failed?; !@failure.nil?; end
|
20
|
+
def errored?; !@raised.nil?; end
|
21
|
+
def passed?; !(failed? || errored?); end
|
28
22
|
def result; @failure || error; end
|
29
23
|
private
|
30
|
-
def actualize(
|
31
|
-
@actual =
|
32
|
-
@default_failure =
|
24
|
+
def actualize(situation, &block)
|
25
|
+
@actual = situation.instance_eval(&block)
|
26
|
+
@default_failure = fail("expected true, not #{@actual.inspect}") unless @actual
|
33
27
|
rescue Exception => e
|
34
28
|
@raised = e
|
35
29
|
end
|
30
|
+
|
31
|
+
def error
|
32
|
+
Error.new("errored with #{@raised}", self, @raised) if errored?
|
33
|
+
end
|
36
34
|
end # Assertion
|
37
35
|
end # Protest
|
data/lib/protest/context.rb
CHANGED
@@ -1,45 +1,57 @@
|
|
1
1
|
module Protest
|
2
2
|
class Context
|
3
|
-
|
3
|
+
# The protein
|
4
|
+
attr_reader :description, :assertions, :situation
|
4
5
|
def initialize(description, reporter, parent=nil)
|
5
|
-
@reporter = reporter
|
6
|
-
@description = description
|
6
|
+
@description, @reporter = description, reporter
|
7
7
|
@assertions = []
|
8
8
|
@parent = parent
|
9
|
-
@
|
10
|
-
bootstrap(
|
9
|
+
@situation = Object.new
|
10
|
+
bootstrap(@situation)
|
11
11
|
end
|
12
12
|
|
13
|
-
def bootstrap(
|
14
|
-
@parent.bootstrap(
|
15
|
-
|
13
|
+
def bootstrap(a_situation)
|
14
|
+
@parent.bootstrap(a_situation) if @parent # Walk it out
|
15
|
+
induce_local_setup(a_situation)
|
16
16
|
end
|
17
17
|
|
18
|
+
# something smelly between setup() and bootstrap()
|
18
19
|
def setup(&block)
|
19
20
|
@setup = block
|
20
|
-
|
21
|
+
make_situation_topical(induce_local_setup(situation))
|
21
22
|
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
# DSLee stuff
|
25
25
|
def context(description, &block) Protest.context(description, @reporter, self, &block); end
|
26
26
|
def asserts(description, &block) new_assertion("asserts #{description}", &block); end
|
27
27
|
def should(description, &block) new_assertion("should #{description}", &block); end
|
28
28
|
|
29
|
+
# In conclusion
|
29
30
|
def report
|
31
|
+
# we should just be passing assertions to the reporter and building better descriptions earlier
|
30
32
|
assertions.each do |assertion|
|
31
33
|
if assertion.passed?
|
32
34
|
@reporter.passed
|
33
35
|
else
|
34
36
|
result = assertion.result.contextualize(self)
|
35
|
-
@reporter.
|
36
|
-
@reporter.failed(result) if assertion.failure?
|
37
|
+
@reporter.send( (assertion.errored? ? :errored : :failed), result)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
41
|
+
|
42
|
+
def to_s; @to_s ||= [@parent.to_s, @description].join(' ').strip; end
|
40
43
|
private
|
41
44
|
def new_assertion(description, &block)
|
42
|
-
(assertions << Assertion.new(description,
|
45
|
+
(assertions << Assertion.new(description, @situation, &block)).last
|
46
|
+
end
|
47
|
+
|
48
|
+
def induce_local_setup(a_situation)
|
49
|
+
a_situation.instance_eval(&@setup) if @setup
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_situation_topical(topic)
|
53
|
+
situation.instance_variable_set(:@topic, topic)
|
54
|
+
situation.instance_eval { def topic; @topic; end }
|
43
55
|
end
|
44
56
|
end # Context
|
45
57
|
end # Protest
|
data/lib/protest/macros.rb
CHANGED
@@ -4,22 +4,22 @@ module Protest
|
|
4
4
|
# asserts("test") { "foo" }.equals("foo")
|
5
5
|
# should("test") { "foo" }.equals("foo")
|
6
6
|
def equals(expected)
|
7
|
-
expected == actual ||
|
7
|
+
expected == actual || fail("expected #{expected.inspect}, not #{actual.inspect}")
|
8
8
|
end
|
9
9
|
|
10
10
|
# Asserts that the result of the test is nil
|
11
11
|
# asserts("test") { nil }.nil
|
12
12
|
# should("test") { nil }.nil
|
13
13
|
def nil
|
14
|
-
actual.nil? ||
|
14
|
+
actual.nil? || fail("expected nil, not #{actual.inspect}")
|
15
15
|
end
|
16
16
|
|
17
17
|
# Asserts that the test raises the expected Exception
|
18
18
|
# asserts("test") { raise My::Exception }.raises(My::Exception)
|
19
19
|
# should("test") { raise My::Exception }.raises(My::Exception)
|
20
20
|
def raises(expected)
|
21
|
-
|
22
|
-
|
21
|
+
fail("should have raised #{expected}, but raised nothing") unless raised
|
22
|
+
fail("should have raised #{expected}, not #{error.class}") unless expected == raised.class
|
23
23
|
@raised = nil
|
24
24
|
end
|
25
25
|
|
@@ -28,14 +28,14 @@ module Protest
|
|
28
28
|
# should("test") { "12345" }.matches(/\d+/)
|
29
29
|
def matches(expected)
|
30
30
|
expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
|
31
|
-
actual =~ expected ||
|
31
|
+
actual =~ expected || fail("expected #{expected.inspect} to match #{actual.inspect}")
|
32
32
|
end
|
33
33
|
|
34
34
|
# Asserts that the result of the test is an object that is a kind of the expected type
|
35
35
|
# asserts("test") { "foo" }.kind_of(String)
|
36
36
|
# should("test") { "foo" }.kind_of(String)
|
37
37
|
def kind_of(expected)
|
38
|
-
actual.kind_of?(expected) ||
|
38
|
+
actual.kind_of?(expected) || fail("expected kind of #{expected}, not #{actual.inspect}")
|
39
39
|
end
|
40
40
|
end # AssertionMacros
|
41
41
|
end # Protest
|
data/lib/protest/report.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module Protest
|
2
4
|
class Report
|
3
5
|
attr_reader :bad_results, :passes, :failures, :errors, :time_taken
|
@@ -31,7 +33,7 @@ module Protest
|
|
31
33
|
class TextReport < Report
|
32
34
|
def initialize(writer=nil)
|
33
35
|
super()
|
34
|
-
@writer ||= STDOUT
|
36
|
+
@writer ||= (Protest.silently? ? StringIO.new : STDOUT)
|
35
37
|
end
|
36
38
|
|
37
39
|
def passed
|
@@ -71,9 +73,6 @@ module Protest
|
|
71
73
|
end # TextReport
|
72
74
|
|
73
75
|
class NilReport < Report
|
74
|
-
def passed; end
|
75
|
-
def failed(failure); end
|
76
|
-
def errored(error); end
|
77
76
|
def results; end
|
78
77
|
def time(&block); yield; end
|
79
78
|
end # NilReport
|
data/protest.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "protest"
|
3
|
-
s.version = "0.0.
|
4
|
-
s.date = "2009-
|
3
|
+
s.version = "0.0.8"
|
4
|
+
s.date = "2009-09-12"
|
5
5
|
s.summary = "An extremely fast, expressive, and context-driven unit-testing framework"
|
6
6
|
s.email = %w[gus@gusg.us]
|
7
7
|
s.homepage = "http://github.com/thumblemonks/protest"
|
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.test_files = %w[
|
28
28
|
Rakefile
|
29
29
|
test/assertion_test.rb
|
30
|
+
test/benchmark/simple_context_and_assertions.rb
|
30
31
|
test/context_test.rb
|
31
32
|
]
|
32
33
|
end
|
data/test/assertion_test.rb
CHANGED
@@ -12,11 +12,11 @@ context "basic assertion:" do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
asserts "failure? is true when assertion does not pass" do
|
15
|
-
Protest::Assertion.new("i will pass", fake_context) { false }.
|
15
|
+
Protest::Assertion.new("i will pass", fake_context) { false }.failed?
|
16
16
|
end
|
17
17
|
|
18
18
|
asserts "error? is true when an unexpected Exception is raised" do
|
19
|
-
Protest::Assertion.new("error", fake_context) { raise Exception, "blah" }.
|
19
|
+
Protest::Assertion.new("error", fake_context) { raise Exception, "blah" }.errored?
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
$:.concat ['./lib']
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Model
|
6
|
+
|
7
|
+
class Room
|
8
|
+
attr_reader :name
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Protest
|
16
|
+
|
17
|
+
require 'protest'
|
18
|
+
Protest.silently! # Do this before any contexts are defined
|
19
|
+
|
20
|
+
context "a room" do
|
21
|
+
setup { @room = Room.new("bed") }
|
22
|
+
|
23
|
+
asserts("name") { @room.name }.equals("bed")
|
24
|
+
end # a room
|
25
|
+
|
26
|
+
#
|
27
|
+
# Test::Unit
|
28
|
+
|
29
|
+
require 'test/unit'
|
30
|
+
Test::Unit.run = false
|
31
|
+
|
32
|
+
require 'test/unit/ui/console/testrunner'
|
33
|
+
|
34
|
+
class RoomTest < Test::Unit::TestCase
|
35
|
+
def setup
|
36
|
+
@room = Room.new("bed")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_room_should_be_named_bed
|
40
|
+
assert_equal "bed", @room.name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Shoulda
|
46
|
+
|
47
|
+
require 'rubygems'
|
48
|
+
require 'shoulda'
|
49
|
+
|
50
|
+
class ShouldaRoomTest < Test::Unit::TestCase
|
51
|
+
def setup
|
52
|
+
@room = Room.new("bed")
|
53
|
+
end
|
54
|
+
|
55
|
+
should("be named 'bed'") { assert_equal "bed", @room.name }
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Benchmarking
|
60
|
+
|
61
|
+
n = 100 * 100
|
62
|
+
|
63
|
+
Benchmark.bmbm do |x|
|
64
|
+
x.report("Protest") do
|
65
|
+
n.times { Protest.report }
|
66
|
+
end
|
67
|
+
|
68
|
+
x.report("Test::Unit") do
|
69
|
+
n.times { Test::Unit::UI::Console::TestRunner.new(RoomTest, Test::Unit::UI::SILENT) }
|
70
|
+
end
|
71
|
+
|
72
|
+
x.report("Shoulda") do
|
73
|
+
n.times { Test::Unit::UI::Console::TestRunner.new(ShouldaRoomTest, Test::Unit::UI::SILENT) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
data/test/context_test.rb
CHANGED
@@ -7,19 +7,26 @@ context "any context" do
|
|
7
7
|
@context = Protest::Context.new("a", @reporter)
|
8
8
|
end
|
9
9
|
|
10
|
-
# denies("two contexts with same name are the same").equals(@context) { Protest::Context.new("a") }
|
11
|
-
|
12
10
|
context "that doesn't have passing tests" do
|
13
11
|
setup do
|
14
12
|
@context.should("a") { true }
|
15
13
|
@context.should("b") { false }
|
16
14
|
@context.should("c") { raise Exception, "blah" }
|
15
|
+
@context.report
|
17
16
|
end
|
18
17
|
|
19
18
|
asserts("passed test count") { @reporter.passes }.equals(1)
|
20
19
|
asserts("failure count") { @reporter.failures }.equals(1)
|
21
20
|
asserts("unexpected errors count") { @reporter.errors }.equals(1)
|
22
21
|
end # that doesn't have passing tests
|
22
|
+
|
23
|
+
context "when running setup:" do
|
24
|
+
setup { @context.setup { "foo" } }
|
25
|
+
|
26
|
+
asserts "topic becomes available to test as result of setup" do
|
27
|
+
@context.should("bar") { topic }.actual
|
28
|
+
end.equals("foo")
|
29
|
+
end # when running setup
|
23
30
|
end # any context
|
24
31
|
|
25
32
|
#
|
@@ -36,8 +43,8 @@ context "test context" do
|
|
36
43
|
should("confirm context description") { test_context.to_s }.equals("foo")
|
37
44
|
should("confirm assertion count") { test_context.assertions.length }.equals(2)
|
38
45
|
|
39
|
-
should("call setup once") do
|
40
|
-
test_context.instance_variable_get(:@test_counter)
|
46
|
+
should("call setup once per context") do
|
47
|
+
test_context.situation.instance_variable_get(:@test_counter) # yuck
|
41
48
|
end.equals(2)
|
42
49
|
end # test context
|
43
50
|
|
@@ -67,12 +74,12 @@ context "nested context" do
|
|
67
74
|
end
|
68
75
|
|
69
76
|
should("inherit parent context") do
|
70
|
-
inner_nested_context.instance_variable_get(:@test_counter)
|
77
|
+
inner_nested_context.situation.instance_variable_get(:@test_counter)
|
71
78
|
end.equals(10)
|
72
79
|
|
73
80
|
should("chain context names") { inner_nested_context.to_s }.equals("foo baz")
|
74
81
|
|
75
82
|
asserts "parent setup is called even if setup not defined for self" do
|
76
|
-
other_nested_context.instance_variable_get(:@foo)
|
83
|
+
other_nested_context.situation.instance_variable_get(:@foo)
|
77
84
|
end.equals("bar")
|
78
85
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thumblemonks-protest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Knowlden
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -33,7 +33,6 @@ files:
|
|
33
33
|
- protest.gemspec
|
34
34
|
has_rdoc: false
|
35
35
|
homepage: http://github.com/thumblemonks/protest
|
36
|
-
licenses:
|
37
36
|
post_install_message:
|
38
37
|
rdoc_options:
|
39
38
|
- --main
|
@@ -55,11 +54,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
54
|
requirements: []
|
56
55
|
|
57
56
|
rubyforge_project:
|
58
|
-
rubygems_version: 1.
|
57
|
+
rubygems_version: 1.2.0
|
59
58
|
signing_key:
|
60
59
|
specification_version: 2
|
61
60
|
summary: An extremely fast, expressive, and context-driven unit-testing framework
|
62
61
|
test_files:
|
63
62
|
- Rakefile
|
64
63
|
- test/assertion_test.rb
|
64
|
+
- test/benchmark/simple_context_and_assertions.rb
|
65
65
|
- test/context_test.rb
|