thumblemonks-protest 0.0.6 → 0.0.7
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/MIT-LICENSE +1 -1
- data/README.markdown +14 -5
- data/Rakefile +6 -1
- data/lib/protest/assertion.rb +3 -26
- data/lib/protest/context.rb +4 -4
- data/lib/protest/macros.rb +5 -0
- data/lib/protest/report.rb +3 -2
- data/lib/protest.rb +4 -0
- data/protest.gemspec +2 -2
- data/test/assertion_test.rb +17 -30
- data/test/context_test.rb +16 -22
- metadata +4 -3
data/MIT-LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Protest
|
2
2
|
|
3
|
-
An extremely fast, expressive, and context-driven unit-testing framework.
|
3
|
+
An extremely fast, expressive, and context-driven unit-testing framework. Protest the slow test.
|
4
4
|
|
5
5
|
#### Example: Basic booleans
|
6
6
|
|
7
7
|
**NOTE:** For no specific reason, I'm going to use an ActiveRecord model in the following examples.
|
8
8
|
|
9
|
-
At it's very basic, Protest simply tries to assert that an expression is true or false. Protest does this through its `asserts`
|
9
|
+
At it's very basic, Protest simply tries to assert that an expression is true or false. Protest does this through its `asserts` or `should` tests. An `asserts` test will pass if the result of running the test is neither `nil` or `false`. A `should` test does the exact same thing - they are in fact aliases. The only difference is in the way you write the assertion.
|
10
10
|
|
11
11
|
For instance, given a test file named `foo_test.rb`, you might have the following code in it:
|
12
12
|
|
@@ -15,10 +15,20 @@ For instance, given a test file named `foo_test.rb`, you might have the followin
|
|
15
15
|
context "a new user" do
|
16
16
|
setup { @user = User.new }
|
17
17
|
asserts("that it is not yet created") { @user.new_record? }
|
18
|
-
denies("that it is valid") { @user.valid? }
|
19
18
|
end
|
20
19
|
|
21
|
-
Notice that you do not define a class anywhere. That would be the entire contents of that test file.
|
20
|
+
Notice that you do not define a class anywhere. That would be the entire contents of that test file. If you wanted to use a `should` instead, you could say this:
|
21
|
+
|
22
|
+
require 'protest'
|
23
|
+
|
24
|
+
context "a new user" do
|
25
|
+
setup { @user = User.new }
|
26
|
+
should("not be created") { @user.new_record? }
|
27
|
+
end
|
28
|
+
|
29
|
+
Sometimes it's more clear to say "this **should** be that" and sometimes it's better to say "**asserts** this is that". I promise you that Protest will get no more redundant than this, but also that besides speed, Protest will aim at being expressive with a minimal amount of syntax.
|
30
|
+
|
31
|
+
I'm going to use `asserts` for the rest of this introduction, but you should know that you can replace any instance of `asserts` with `should` and nothing would change.
|
22
32
|
|
23
33
|
#### Example: Equality
|
24
34
|
|
@@ -275,7 +285,6 @@ TONS OF STUFF
|
|
275
285
|
1. Refactor reporting; some abstracting is needed for recording a result (for instance)
|
276
286
|
1. Need to know where in backtrace a test failed (line number, etc.)
|
277
287
|
1. More assertion macros: throws, etc.
|
278
|
-
1. Handle denies macro different, so that an entire failure message can translated to the 'negative' assertion. I don't want to add deny\_this and deny\_that macros
|
279
288
|
1. Aliases for context "with, without, when, ..."; add those words to test description
|
280
289
|
1. Optimization and simplification (ex. flog is complaining print\_result\_stack)
|
281
290
|
1. Better error messages (maybe need to rename asserts to should for better readability)
|
data/Rakefile
CHANGED
@@ -10,7 +10,12 @@ task :test do
|
|
10
10
|
Protest.report
|
11
11
|
end
|
12
12
|
|
13
|
-
desc "Run
|
13
|
+
desc "Run Flog against library (except tests)"
|
14
14
|
task :flog do
|
15
15
|
puts %x[find ./lib -name *.rb | xargs flog]
|
16
16
|
end
|
17
|
+
|
18
|
+
desc "Run Roodi against library (except tests)"
|
19
|
+
task :roodi do
|
20
|
+
puts %x[find ./lib -name *.rb | xargs roodi]
|
21
|
+
end
|
data/lib/protest/assertion.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Protest
|
2
2
|
|
3
3
|
class Assertion
|
4
|
-
attr_reader :raised
|
4
|
+
attr_reader :raised, :to_s
|
5
5
|
def initialize(description, target, &block)
|
6
|
-
@description = description
|
6
|
+
@description = @to_s = description
|
7
7
|
actualize(target, &block)
|
8
8
|
end
|
9
9
|
|
@@ -26,35 +26,12 @@ module Protest
|
|
26
26
|
def error?; !failure? && raised; end
|
27
27
|
def passed?; !failure? && !error?; end
|
28
28
|
def result; @failure || error; end
|
29
|
-
def to_s; @description; end
|
30
29
|
private
|
31
30
|
def actualize(target, &block)
|
32
31
|
@actual = target.instance_eval(&block)
|
33
|
-
@default_failure =
|
32
|
+
@default_failure = failure("expected true, not #{@actual.inspect}") unless @actual
|
34
33
|
rescue Exception => e
|
35
34
|
@raised = e
|
36
35
|
end
|
37
|
-
|
38
|
-
def base_assertion
|
39
|
-
failure("expected true, not #{@actual.inspect}") unless @actual
|
40
|
-
end
|
41
36
|
end # Assertion
|
42
|
-
|
43
|
-
# Denial will evaulate to true if the assertion failed in some way. Errors pass through. A Failure
|
44
|
-
# is generated if the assertion actually passed.
|
45
|
-
class Denial < Assertion
|
46
|
-
def actual
|
47
|
-
@actual # Do not forget default failure unless a failure is thrown
|
48
|
-
end
|
49
|
-
|
50
|
-
alias :actual_failure :failure
|
51
|
-
def failure(message)
|
52
|
-
@default_failure = @failure = nil # this is a good thing
|
53
|
-
end
|
54
|
-
private
|
55
|
-
def base_assertion
|
56
|
-
actual_failure("expected assertion to fail") if @actual
|
57
|
-
end
|
58
|
-
end # Denial
|
59
|
-
|
60
37
|
end # Protest
|
data/lib/protest/context.rb
CHANGED
@@ -23,8 +23,8 @@ module Protest
|
|
23
23
|
def to_s; @to_s ||= [@parent.to_s, @description].join(' ').strip; end
|
24
24
|
|
25
25
|
def context(description, &block) Protest.context(description, @reporter, self, &block); end
|
26
|
-
def asserts(description, &block) new_assertion(
|
27
|
-
def
|
26
|
+
def asserts(description, &block) new_assertion("asserts #{description}", &block); end
|
27
|
+
def should(description, &block) new_assertion("should #{description}", &block); end
|
28
28
|
|
29
29
|
def report
|
30
30
|
assertions.each do |assertion|
|
@@ -38,8 +38,8 @@ module Protest
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
private
|
41
|
-
def new_assertion(
|
42
|
-
(assertions <<
|
41
|
+
def new_assertion(description, &block)
|
42
|
+
(assertions << Assertion.new(description, self, &block)).last
|
43
43
|
end
|
44
44
|
end # Context
|
45
45
|
end # Protest
|
data/lib/protest/macros.rb
CHANGED
@@ -2,18 +2,21 @@ module Protest
|
|
2
2
|
module AssertionMacros
|
3
3
|
# Asserts that the result of the test equals the expected value
|
4
4
|
# asserts("test") { "foo" }.equals("foo")
|
5
|
+
# should("test") { "foo" }.equals("foo")
|
5
6
|
def equals(expected)
|
6
7
|
expected == actual || failure("expected #{expected.inspect}, not #{actual.inspect}")
|
7
8
|
end
|
8
9
|
|
9
10
|
# Asserts that the result of the test is nil
|
10
11
|
# asserts("test") { nil }.nil
|
12
|
+
# should("test") { nil }.nil
|
11
13
|
def nil
|
12
14
|
actual.nil? || failure("expected nil, not #{actual.inspect}")
|
13
15
|
end
|
14
16
|
|
15
17
|
# Asserts that the test raises the expected Exception
|
16
18
|
# asserts("test") { raise My::Exception }.raises(My::Exception)
|
19
|
+
# should("test") { raise My::Exception }.raises(My::Exception)
|
17
20
|
def raises(expected)
|
18
21
|
failure("should have raised #{expected}, but raised nothing") unless raised
|
19
22
|
failure("should have raised #{expected}, not #{error.class}") unless expected == raised.class
|
@@ -22,6 +25,7 @@ module Protest
|
|
22
25
|
|
23
26
|
# Asserts that the result of the test equals matches against the proved expression
|
24
27
|
# asserts("test") { "12345" }.matches(/\d+/)
|
28
|
+
# should("test") { "12345" }.matches(/\d+/)
|
25
29
|
def matches(expected)
|
26
30
|
expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
|
27
31
|
actual =~ expected || failure("expected #{expected.inspect} to match #{actual.inspect}")
|
@@ -29,6 +33,7 @@ module Protest
|
|
29
33
|
|
30
34
|
# Asserts that the result of the test is an object that is a kind of the expected type
|
31
35
|
# asserts("test") { "foo" }.kind_of(String)
|
36
|
+
# should("test") { "foo" }.kind_of(String)
|
32
37
|
def kind_of(expected)
|
33
38
|
actual.kind_of?(expected) || failure("expected kind of #{expected}, not #{actual.inspect}")
|
34
39
|
end
|
data/lib/protest/report.rb
CHANGED
@@ -59,13 +59,14 @@ module Protest
|
|
59
59
|
def print_result_stack
|
60
60
|
bad_results.each_with_index do |result, idx|
|
61
61
|
@writer.puts render_result(idx + 1, result)
|
62
|
-
@writer.puts " " + result.backtrace.join("\n ")
|
62
|
+
@writer.puts " " + result.backtrace.join("\n ") if result.print_stacktrace?
|
63
|
+
@writer.puts "\n\n"
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
66
67
|
def render_result(idx, result)
|
67
68
|
format_args = [idx, result.context.to_s, result.assertion.to_s, result.to_s]
|
68
|
-
"#%d - %s
|
69
|
+
"#%d - %s %s: %s" % format_args
|
69
70
|
end
|
70
71
|
end # TextReport
|
71
72
|
|
data/lib/protest.rb
CHANGED
@@ -42,16 +42,20 @@ module Protest
|
|
42
42
|
super(message)
|
43
43
|
@assertion = assertion
|
44
44
|
end
|
45
|
+
|
45
46
|
def contextualize(ctx)
|
46
47
|
@context = ctx
|
47
48
|
self
|
48
49
|
end
|
50
|
+
|
51
|
+
def print_stacktrace?; false; end
|
49
52
|
end
|
50
53
|
class Error < Failure
|
51
54
|
def initialize(message, assertion, error)
|
52
55
|
super(message, assertion)
|
53
56
|
set_backtrace(error.backtrace)
|
54
57
|
end
|
58
|
+
def print_stacktrace?; true; end
|
55
59
|
end
|
56
60
|
end # Protest
|
57
61
|
|
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.7"
|
4
|
+
s.date = "2009-08-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"
|
data/test/assertion_test.rb
CHANGED
@@ -1,79 +1,66 @@
|
|
1
1
|
require 'protest'
|
2
2
|
|
3
|
-
fake_context = Object.new
|
3
|
+
fake_context = Object.new # It works ... so, why not?
|
4
4
|
|
5
5
|
context "basic assertion:" do
|
6
|
-
|
6
|
+
should "have a description" do
|
7
7
|
Protest::Assertion.new("i will pass", fake_context).to_s
|
8
8
|
end.equals("i will pass")
|
9
9
|
|
10
|
-
asserts
|
10
|
+
asserts "pass? is true when assertion passed" do
|
11
11
|
Protest::Assertion.new("i will pass", fake_context) { true }.passed?
|
12
12
|
end
|
13
13
|
|
14
|
-
asserts
|
14
|
+
asserts "failure? is true when assertion does not pass" do
|
15
15
|
Protest::Assertion.new("i will pass", fake_context) { false }.failure?
|
16
16
|
end
|
17
17
|
|
18
|
-
asserts
|
18
|
+
asserts "error? is true when an unexpected Exception is raised" do
|
19
19
|
Protest::Assertion.new("error", fake_context) { raise Exception, "blah" }.error?
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
context "basic denial:" do
|
24
|
-
asserts("false assertion passes") do
|
25
|
-
Protest::Denial.new("i will pass", fake_context) { false }.passed?
|
26
|
-
end
|
27
|
-
|
28
|
-
asserts("true assertion fails") do
|
29
|
-
Protest::Denial.new("i will not pass", fake_context) { true }.failure?
|
30
|
-
end
|
31
|
-
end # basic assertion
|
32
|
-
|
33
23
|
context "equals assertion:" do
|
34
|
-
asserts
|
24
|
+
asserts "result equals expectation" do
|
35
25
|
Protest::Assertion.new("i will pass", fake_context) { "foo bar" }.equals("foo bar")
|
36
26
|
end
|
37
27
|
|
38
|
-
|
28
|
+
should "raise a Failure if results don't equal each other" do
|
39
29
|
Protest::Assertion.new("failure", fake_context) { "bar" }.equals("foo")
|
40
30
|
end.kind_of(Protest::Failure)
|
41
|
-
|
42
|
-
asserts("a non-matching string is a good thing when in denial") do
|
43
|
-
Protest::Denial.new("pass", fake_context) { "bar" }.equals("foo")
|
44
|
-
end.nil
|
45
31
|
end # equals assertion
|
46
32
|
|
47
33
|
context "nil assertion:" do
|
48
|
-
asserts("
|
49
|
-
|
34
|
+
asserts("result is nil") { Protest::Assertion.new("foo", fake_context) { nil }.nil }
|
35
|
+
should "raise a Failure if not nil" do
|
50
36
|
Protest::Assertion.new("foo", fake_context) { "a" }.nil
|
51
37
|
end.kind_of(Protest::Failure)
|
52
38
|
end # nil assertion
|
53
39
|
|
54
40
|
context "raises assertion:" do
|
55
|
-
|
56
|
-
end #
|
41
|
+
should("raise an Exception") { raise Exception }.raises(Exception)
|
42
|
+
end # raises assertion
|
57
43
|
|
58
44
|
context "matching assertion:" do
|
59
|
-
asserts
|
45
|
+
asserts "result matches expression" do
|
60
46
|
Protest::Assertion.new("foo", fake_context) { "a" }.matches(%r[.])
|
61
47
|
end.equals(0)
|
62
48
|
|
63
|
-
|
49
|
+
should "raise a Failure if result does not match" do
|
64
50
|
Protest::Assertion.new("foo", fake_context) { "" }.matches(%r[.])
|
65
51
|
end.kind_of(Protest::Failure)
|
66
52
|
|
67
|
-
|
53
|
+
should "return the result of a matching operation" do
|
68
54
|
Protest::Assertion.new("foo", fake_context) { "a" }.matches("a")
|
69
55
|
end.equals(0)
|
70
56
|
end # maching assertion
|
71
57
|
|
72
58
|
context "kind_of assertion:" do
|
73
|
-
asserts
|
59
|
+
asserts "specific result is a kind of String" do
|
74
60
|
Protest::Assertion.new("foo", fake_context) { "a" }.kind_of(String)
|
75
61
|
end
|
76
|
-
|
62
|
+
|
63
|
+
should "raise a Failure if not a kind of String" do
|
77
64
|
Protest::Assertion.new("foo", fake_context) { 0 }.kind_of(String)
|
78
65
|
end.kind_of(Protest::Failure)
|
79
66
|
end # kind_of assertion
|
data/test/context_test.rb
CHANGED
@@ -11,41 +11,35 @@ context "any context" do
|
|
11
11
|
|
12
12
|
context "that doesn't have passing tests" do
|
13
13
|
setup do
|
14
|
-
@context.
|
15
|
-
@context.
|
16
|
-
@context.
|
14
|
+
@context.should("a") { true }
|
15
|
+
@context.should("b") { false }
|
16
|
+
@context.should("c") { raise Exception, "blah" }
|
17
17
|
end
|
18
18
|
|
19
|
-
asserts("
|
20
|
-
asserts("
|
21
|
-
asserts("
|
19
|
+
asserts("passed test count") { @reporter.passes }.equals(1)
|
20
|
+
asserts("failure count") { @reporter.failures }.equals(1)
|
21
|
+
asserts("unexpected errors count") { @reporter.errors }.equals(1)
|
22
22
|
end # that doesn't have passing tests
|
23
23
|
end # any context
|
24
24
|
|
25
|
-
context "when denying things" do
|
26
|
-
denies("true is false") { false }
|
27
|
-
denies("bar equals foo") { "bar" }.equals("foo")
|
28
|
-
denies("bar matches only digits") { "bar" }.matches(/^\d+$/)
|
29
|
-
end
|
30
|
-
|
31
25
|
#
|
32
26
|
# Test Context
|
33
27
|
|
34
28
|
test_context = context("foo", Protest::NilReport.new) do
|
35
29
|
setup { @test_counter = 0 }
|
36
|
-
asserts("
|
37
|
-
asserts("
|
30
|
+
asserts("truthiness") { @test_counter += 1; true }
|
31
|
+
asserts("more truthiness") { @test_counter += 1; true }
|
38
32
|
end # A CONTEXT THAT IS DEQUEUED
|
39
33
|
|
40
34
|
context "test context" do
|
41
35
|
setup { Protest.dequeue_context(test_context) }
|
42
|
-
|
43
|
-
|
36
|
+
should("confirm context description") { test_context.to_s }.equals("foo")
|
37
|
+
should("confirm assertion count") { test_context.assertions.length }.equals(2)
|
44
38
|
|
45
|
-
|
39
|
+
should("call setup once") do
|
46
40
|
test_context.instance_variable_get(:@test_counter)
|
47
41
|
end.equals(2)
|
48
|
-
end
|
42
|
+
end # test context
|
49
43
|
|
50
44
|
#
|
51
45
|
# Nested Context
|
@@ -56,7 +50,7 @@ nested_context = context("foo", Protest::NilReport.new) do
|
|
56
50
|
@test_counter = 0
|
57
51
|
@foo = "bar"
|
58
52
|
end
|
59
|
-
asserts("
|
53
|
+
asserts("truthiness") { @test_counter += 1; true }
|
60
54
|
|
61
55
|
inner_nested_context = context("baz") do
|
62
56
|
setup { @test_counter += 10 }
|
@@ -72,13 +66,13 @@ context "nested context" do
|
|
72
66
|
end
|
73
67
|
end
|
74
68
|
|
75
|
-
|
69
|
+
should("inherit parent context") do
|
76
70
|
inner_nested_context.instance_variable_get(:@test_counter)
|
77
71
|
end.equals(10)
|
78
72
|
|
79
|
-
|
73
|
+
should("chain context names") { inner_nested_context.to_s }.equals("foo baz")
|
80
74
|
|
81
|
-
asserts
|
75
|
+
asserts "parent setup is called even if setup not defined for self" do
|
82
76
|
other_nested_context.instance_variable_get(:@foo)
|
83
77
|
end.equals("bar")
|
84
78
|
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.7
|
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-08-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -33,6 +33,7 @@ files:
|
|
33
33
|
- protest.gemspec
|
34
34
|
has_rdoc: false
|
35
35
|
homepage: http://github.com/thumblemonks/protest
|
36
|
+
licenses:
|
36
37
|
post_install_message:
|
37
38
|
rdoc_options:
|
38
39
|
- --main
|
@@ -54,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
55
|
requirements: []
|
55
56
|
|
56
57
|
rubyforge_project:
|
57
|
-
rubygems_version: 1.
|
58
|
+
rubygems_version: 1.3.5
|
58
59
|
signing_key:
|
59
60
|
specification_version: 2
|
60
61
|
summary: An extremely fast, expressive, and context-driven unit-testing framework
|