graham 0.0.3a → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +30 -10
- data/lib/graham.rb +15 -37
- data/lib/graham/dsl.rb +27 -11
- data/lib/graham/pp.rb +4 -4
- data/lib/graham/test_case.rb +26 -0
- data/lib/graham/version.rb +1 -1
- data/test/case/cases.rb +9 -11
- data/test/case/control.rb +1 -1
- data/test/case/subject.rb +3 -0
- data/test/unit/arguments.rb +1 -1
- data/test/unit/namespaces.rb +1 -4
- metadata +7 -4
data/README.md
CHANGED
@@ -1,13 +1,29 @@
|
|
1
|
-
|
1
|
+
Graham
|
2
|
+
------
|
2
3
|
|
3
|
-
Graham is a
|
4
|
+
Graham is a small-but-strong testing library based on Mallow - in fact it _is_ Mallow, with a tweaked DSL and some extra exception handling, bundled with a trivial pretty printer and a helper for Rake tasks. It was written to handle Mallow's unit tests because:
|
4
5
|
* Test::Unit was too ugly
|
5
6
|
* TestRocket was too minimal
|
6
7
|
* RSpec was too verbose (not to mention ridiculous overkill)
|
7
8
|
|
8
|
-
|
9
|
+
Features
|
10
|
+
--------
|
9
11
|
|
10
|
-
|
12
|
+
* Dandy DSL
|
13
|
+
* Zero namespace pollution
|
14
|
+
* Hackable with a small code footprint
|
15
|
+
* Testing paradigm does not necessitate the gratuitous reinvention of such language features as inheritance, namespacing, and variable assignment (unlike some other frameworks that i sometimes have to use :/)
|
16
|
+
|
17
|
+
But how to use ???
|
18
|
+
----------------------
|
19
|
+
|
20
|
+
Graham test cases are just ordinary methods on arbitrary objects:
|
21
|
+
```ruby
|
22
|
+
Graham.test {|that|
|
23
|
+
that[1].even?.is true
|
24
|
+
} #=> {#<TestCase ...> => false}
|
25
|
+
```
|
26
|
+
You can optionally specify a default receiver for tests:
|
11
27
|
```ruby
|
12
28
|
class Cases
|
13
29
|
def initialize
|
@@ -23,14 +39,18 @@ Graham test cases are instance methods on arbitrary classes:
|
|
23
39
|
obj.upcase
|
24
40
|
end
|
25
41
|
end
|
26
|
-
|
27
|
-
|
28
|
-
```ruby
|
29
|
-
Graham.test(Cases) do |that|
|
42
|
+
|
43
|
+
Graham.test(Cases.new) do |that|
|
30
44
|
that.one_squared.is 1
|
31
45
|
that.dividing_by_zero.returns_a(Fixnum).such_that {|n| n > 1}
|
32
46
|
that.calling_upcase_on(Graham).does_not_raise_an_exception
|
33
|
-
end
|
47
|
+
end
|
34
48
|
```
|
35
|
-
|
49
|
+
See RDoc documentation for more details on usage, and for information on how to use the Rake helper. Hint:
|
50
|
+
```ruby
|
51
|
+
require 'graham/rake_task'
|
52
|
+
Graham::RakeTask.new
|
53
|
+
task default: :test
|
54
|
+
```
|
55
|
+
|
36
56
|
|
data/lib/graham.rb
CHANGED
@@ -3,49 +3,27 @@ require 'mallow'
|
|
3
3
|
require 'graham/version'
|
4
4
|
# == A miniature test engine powered by Mallow
|
5
5
|
# ---
|
6
|
-
# Test cases are
|
7
|
-
# their return values are enumerated using a
|
6
|
+
# Test cases are methods on arbitrary objects, and expectations on
|
7
|
+
# their return values are enumerated using a slightly modified
|
8
8
|
# Mallow::DSL.
|
9
9
|
#
|
10
|
-
# class
|
11
|
-
# def four_plus_five
|
12
|
-
# 4 + 5
|
13
|
-
# end
|
10
|
+
# class MyCase
|
14
11
|
# def upcasing(s)
|
15
12
|
# s.upcase
|
16
13
|
# end
|
17
|
-
# def one_divided_by_zero
|
18
|
-
# 1/0
|
19
|
-
# end
|
20
14
|
# end
|
21
15
|
#
|
22
|
-
# Graham.test(
|
23
|
-
# that.four_plus_five.is 9
|
16
|
+
# Graham.test(MyCase.new) { |that|
|
24
17
|
# that.upcasing('test').returns 'TeST'
|
25
|
-
#
|
26
|
-
# } #=> {:four_plus_five=>true, :upcasing=>false, :one_divided_by_zero=>#<ZeroDivisionError>}
|
27
|
-
#
|
28
|
-
# === N.B.
|
29
|
-
# Since a Graham test is basically a Mallow pattern matcher, only the first
|
30
|
-
# test on a case will actually be executed, as subsequent invocations of the
|
31
|
-
# test case will be matched by the first rule. This can lead to confusing
|
32
|
-
# results:
|
18
|
+
# } #=> {#<TestCase ...>=>false}
|
33
19
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# that.four_plus_five.returns_a(String)
|
37
|
-
# } #=> {:four_plus_five=>true}
|
20
|
+
# The argument to Graham::test is optional, but if you include it,
|
21
|
+
# you don't need to specify test subjects. Compare:
|
38
22
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# Or (better) chain the tests you want to run:
|
45
|
-
#
|
46
|
-
# Graham.test(Cases) { |that|
|
47
|
-
# that.four_plus_five.returns_a(Fixnum).that {is_a? String}
|
48
|
-
# } #=> {:four_plus_five=>false}
|
23
|
+
# Graham.test! { |that|
|
24
|
+
# that[1].odd?.returns true
|
25
|
+
# that['adsf'].is_a? String
|
26
|
+
# }
|
49
27
|
#
|
50
28
|
module Graham
|
51
29
|
autoload :PP, 'graham/pp'
|
@@ -55,12 +33,12 @@ module Graham
|
|
55
33
|
# A convenience method that builds and executes a Graham::Core
|
56
34
|
# in the given namespace (defaults to Cases). See documentation for
|
57
35
|
# Graham for more on usage.
|
58
|
-
def test(ns, &b)
|
36
|
+
def test(ns=nil, &b)
|
59
37
|
DSL.build_core(ns, &b).test
|
60
38
|
end
|
61
39
|
# A convenience method that calls ::test and passes the output to a
|
62
40
|
# pretty printer.
|
63
|
-
def pp(ns, &b)
|
41
|
+
def pp(ns=nil, &b)
|
64
42
|
PP.new(test ns,&b).pp
|
65
43
|
end
|
66
44
|
alias test! pp
|
@@ -69,7 +47,7 @@ module Graham
|
|
69
47
|
class Core < Mallow::Core
|
70
48
|
attr_reader :cases
|
71
49
|
def initialize
|
72
|
-
@cases =
|
50
|
+
@cases = []
|
73
51
|
super
|
74
52
|
end
|
75
53
|
|
@@ -84,7 +62,7 @@ module Graham
|
|
84
62
|
end
|
85
63
|
end
|
86
64
|
|
87
|
-
def test; Hash[_fluff @cases
|
65
|
+
def test; Hash[_fluff @cases] end
|
88
66
|
end
|
89
67
|
end
|
90
68
|
|
data/lib/graham/dsl.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'graham/test_case'
|
1
2
|
module Graham
|
2
3
|
class DSL < Mallow::BasicDSL
|
3
4
|
include Mallow::DSL::Matchers
|
4
5
|
def self.build_core(ns)
|
5
6
|
yield(dsl = new(ns))
|
6
|
-
dsl.
|
7
|
+
dsl.send(:rule!).core
|
7
8
|
end
|
8
9
|
|
9
10
|
def initialize(ns)
|
@@ -16,23 +17,26 @@ module Graham
|
|
16
17
|
when /^((and|that)_)+(.+)$/
|
17
18
|
respond_to?($3)? send($3, *args, &b) : super
|
18
19
|
else
|
19
|
-
|
20
|
-
(conditions.empty?? self : rule!)._where {|e|e==msg}
|
20
|
+
_case @ns, msg, args, b
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
# Add a condition on a test case's return value. If the given block
|
25
|
+
# has no parameters, it is evaluated in the context of the return
|
26
|
+
# value.
|
27
|
+
def where(&b)
|
28
|
+
push {|e| preproc(b).call e.go }
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
# Specify the subject for the next test.
|
32
|
+
def subject(obj)
|
33
|
+
TestCase::Proxy.new self, obj
|
30
34
|
end
|
31
35
|
|
32
36
|
def raises(x=nil)
|
33
|
-
|
37
|
+
push {|tc|
|
34
38
|
begin
|
35
|
-
|
39
|
+
tc.go
|
36
40
|
false
|
37
41
|
rescue x => e
|
38
42
|
true
|
@@ -43,9 +47,9 @@ module Graham
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def does_not_raise(x=nil)
|
46
|
-
|
50
|
+
push {|tc|
|
47
51
|
begin
|
48
|
-
|
52
|
+
tc.go
|
49
53
|
true
|
50
54
|
rescue x => e
|
51
55
|
false
|
@@ -78,6 +82,18 @@ module Graham
|
|
78
82
|
alias returns_a a
|
79
83
|
alias returns_an a
|
80
84
|
|
85
|
+
alias [] subject
|
86
|
+
|
87
|
+
private
|
88
|
+
def push(&p)
|
89
|
+
conditions << p
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
def _case(obj, msg, args, blk)
|
94
|
+
core.cases << (tc=TestCase.new obj, msg, args, blk)
|
95
|
+
(conditions.empty?? self : rule!).send(:push) {|e|e==tc}
|
96
|
+
end
|
81
97
|
end
|
82
98
|
end
|
83
99
|
|
data/lib/graham/pp.rb
CHANGED
@@ -12,14 +12,14 @@ module Graham
|
|
12
12
|
@results, @bt, @out, @color = results, bt, out, color
|
13
13
|
end
|
14
14
|
def pp
|
15
|
-
results.each do |
|
15
|
+
results.each do |tc, result|
|
16
16
|
out.puts case result
|
17
17
|
when true
|
18
|
-
[hi('PASS', GREEN), lo(
|
18
|
+
[hi('PASS', GREEN), lo(tc)]
|
19
19
|
when false
|
20
|
-
[hi('FAIL', RED), lo(
|
20
|
+
[hi('FAIL', RED), lo(tc), "#=> #{tc.go}"]
|
21
21
|
else
|
22
|
-
[hi('XPTN', RED), lo(
|
22
|
+
[hi('XPTN', RED), lo(tc), result.class.name, result.message] + backtrace(result, bt)
|
23
23
|
end.join ' :: '
|
24
24
|
end
|
25
25
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Graham
|
2
|
+
# A struct for encapsulating test cases. Memoizes the case's return
|
3
|
+
# value.
|
4
|
+
class TestCase < Struct.new(:obj, :msg, :args, :blk)
|
5
|
+
def go
|
6
|
+
defined?(@val) ? @val : (@val = obj.send msg, *args, &blk)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"#{Class===obj ? '::' : ?#}#{msg}(#{args.join ', '})#{" {...}" if blk}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Delegator to create test cases out of methods that would otherwise
|
14
|
+
# mistakenly be called on a DSL instance.
|
15
|
+
class Proxy < BasicObject
|
16
|
+
def initialize(dsl,obj)
|
17
|
+
@dsl,@obj=dsl,obj
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(msg,*args,&blk)
|
21
|
+
@dsl.send :_case,@obj,msg,args,blk
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/lib/graham/version.rb
CHANGED
data/test/case/cases.rb
CHANGED
@@ -3,7 +3,7 @@ class TestCases
|
|
3
3
|
def DocTest2; 'test'.upcase end
|
4
4
|
def DocTest3; 1/0 end
|
5
5
|
def rdoc_example
|
6
|
-
Graham.test(TestCases) { |that|
|
6
|
+
Graham.test(TestCases.new) { |that|
|
7
7
|
that.DocTest1.returns_a(Fixnum).such_that {self < 100}
|
8
8
|
that.DocTest2.returns 'TeST'
|
9
9
|
that.DocTest3.returns_a(Numeric)
|
@@ -21,7 +21,7 @@ class TestCases
|
|
21
21
|
Graham.this_is_not_a_method
|
22
22
|
end
|
23
23
|
def readme_example
|
24
|
-
Graham.test(TestCases) do |that|
|
24
|
+
Graham.test(TestCases.new) do |that|
|
25
25
|
that.squaring(1).returns 1
|
26
26
|
that.dividing_by_zero(1).returns_a(Fixnum)
|
27
27
|
that.calling_a_nonexistent_method.does_not_raise_an_exception
|
@@ -29,16 +29,14 @@ class TestCases
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
Graham.
|
33
|
-
that.rdoc_example.returns_a(Hash).of_size(3).such_that {
|
34
|
-
|
35
|
-
|
36
|
-
h[:DocTest3].is_a? ZeroDivisionError
|
32
|
+
Graham.test!(TestCases.new) do |that|
|
33
|
+
that.rdoc_example.returns_a(Hash).of_size(3).such_that {
|
34
|
+
values[0..1] == [true, false] and
|
35
|
+
values[2].is_a? ZeroDivisionError
|
37
36
|
}
|
38
|
-
that.readme_example.returns_a(Hash).of_size(3).such_that {
|
39
|
-
|
40
|
-
|
41
|
-
h[:calling_a_nonexistent_method] == false
|
37
|
+
that.readme_example.returns_a(Hash).of_size(3).such_that {
|
38
|
+
values.values_at(0,2) == [true, false] and
|
39
|
+
values[1].is_a? ZeroDivisionError
|
42
40
|
}
|
43
41
|
end
|
44
42
|
|
data/test/case/control.rb
CHANGED
@@ -3,7 +3,7 @@ class ControlCases
|
|
3
3
|
def raising_a_name_error; raise Asdf.qwer.ty / 0 end
|
4
4
|
end
|
5
5
|
|
6
|
-
Graham.pp(ControlCases) {|that|
|
6
|
+
Graham.pp(ControlCases.new) {|that|
|
7
7
|
that.the_number_99.returns_a(Fixnum).such_that {self==99}.and_that_is 99
|
8
8
|
that.raising_a_name_error.raises.and_raises_a NameError
|
9
9
|
}
|
data/test/unit/arguments.rb
CHANGED
data/test/unit/namespaces.rb
CHANGED
@@ -14,13 +14,10 @@ class Namespace
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
Graham.pp(Namespace) do |that|
|
17
|
+
Graham.pp(Namespace.new) do |that|
|
18
18
|
that.namespacing.is_such_that {
|
19
19
|
self.class == Namespace
|
20
20
|
}.and {!respond_to? :rdoc_example
|
21
21
|
}.and { respond_to? :namespacing}
|
22
|
-
|
23
|
-
that.initialization.returns 1
|
24
|
-
that.reinitialization.returns 1
|
25
22
|
end
|
26
23
|
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graham
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- feivel jellyfish
|
@@ -40,9 +40,11 @@ files:
|
|
40
40
|
- lib/graham/pp.rb
|
41
41
|
- lib/graham/version.rb
|
42
42
|
- lib/graham/dsl.rb
|
43
|
+
- lib/graham/test_case.rb
|
43
44
|
- Rakefile
|
44
45
|
- test/case/control.rb
|
45
46
|
- test/case/cases.rb
|
47
|
+
- test/case/subject.rb
|
46
48
|
- test/unit/arguments.rb
|
47
49
|
- test/unit/namespaces.rb
|
48
50
|
homepage: http://github.com/gwentacle/graham
|
@@ -60,9 +62,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
63
|
none: false
|
62
64
|
requirements:
|
63
|
-
- - ! '
|
65
|
+
- - ! '>='
|
64
66
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
67
|
+
version: '0'
|
66
68
|
requirements: []
|
67
69
|
rubyforge_project:
|
68
70
|
rubygems_version: 1.8.23
|
@@ -73,5 +75,6 @@ test_files:
|
|
73
75
|
- Rakefile
|
74
76
|
- test/case/control.rb
|
75
77
|
- test/case/cases.rb
|
78
|
+
- test/case/subject.rb
|
76
79
|
- test/unit/arguments.rb
|
77
80
|
- test/unit/namespaces.rb
|