graham 0.0.3a → 0.0.3
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/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
|