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 CHANGED
@@ -1,13 +1,29 @@
1
- # Graham #
1
+ Graham
2
+ ------
2
3
 
3
- Graham is a tiny-yet-useful 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
+ 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
- ## How does one use graham ??? ##
9
+ Features
10
+ --------
9
11
 
10
- Graham test cases are instance methods on arbitrary classes:
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
- Then test your cases:
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 #=> {:one_squared=>true, :dividing_by_zero=>#<ZeroDivisionError>, :calling_upcase_on=>false}
47
+ end
34
48
  ```
35
- Or use Graham::test! to run the results through Graham's pretty printer.
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
 
@@ -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 instance methods on arbitrary classes, and expectations on
7
- # their return values are enumerated using a very slightly modified
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 Cases
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(Cases) { |that|
23
- # that.four_plus_five.is 9
16
+ # Graham.test(MyCase.new) { |that|
24
17
  # that.upcasing('test').returns 'TeST'
25
- # that.one_divided_by_zero.equals :infinity
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
- # Graham.test(Cases) { |that|
35
- # that.four_plus_five.returns_a(Fixnum)
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
- # To avoid this issue, either run separate tests:
40
- #
41
- # Graham.test(Cases) {|that| that.four_plus_five.returns_a Fixnum} #=> {:four_plus_five=>true}
42
- # Graham.test(Cases) {|that| that.four_plus_five.returns_a String} #=> {:four_plus_five=>false}
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.keys] end
65
+ def test; Hash[_fluff @cases] end
88
66
  end
89
67
  end
90
68
 
@@ -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.finish!
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
- core.cases[msg] = args
20
- (conditions.empty?? self : rule!)._where {|e|e==msg}
20
+ _case @ns, msg, args, b
21
21
  end
22
22
  end
23
23
 
24
- def _where(&b)
25
- push b, :conditions
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
- def where(&b)
29
- _where {|e| preproc(b).call @ns.new.send(e, *core.cases[e]) }
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
- _where {
37
+ push {|tc|
34
38
  begin
35
- call
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
- _where {
50
+ push {|tc|
47
51
  begin
48
- call
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
 
@@ -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 |name, result|
15
+ results.each do |tc, result|
16
16
  out.puts case result
17
17
  when true
18
- [hi('PASS', GREEN), lo(name)]
18
+ [hi('PASS', GREEN), lo(tc)]
19
19
  when false
20
- [hi('FAIL', RED), lo(name)]
20
+ [hi('FAIL', RED), lo(tc), "#=> #{tc.go}"]
21
21
  else
22
- [hi('XPTN', RED), lo(name), result.class.name, result.message] + backtrace(result, bt)
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
+
@@ -1,3 +1,3 @@
1
1
  module Graham
2
- VERSION = '0.0.3a'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -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.pp(TestCases) do |that|
33
- that.rdoc_example.returns_a(Hash).of_size(3).such_that {|h|
34
- h[:DocTest1] == true and
35
- h[:DocTest2] == false and
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 {|h|
39
- h[:squaring] == true and
40
- h[:dividing_by_zero].is_a? ZeroDivisionError and
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
 
@@ -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
  }
@@ -0,0 +1,3 @@
1
+ Graham.test! {|that|
2
+ that[1].even?.is false
3
+ }
@@ -4,6 +4,6 @@ class Arguments
4
4
  end
5
5
  end
6
6
 
7
- Graham.pp(Arguments) {|that|
7
+ Graham.pp(Arguments.new) {|that|
8
8
  that.doubling(2).equals 4
9
9
  }
@@ -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.3a
5
- prerelease: 5
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: 1.3.1
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