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 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