gimme 0.1.6

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.
@@ -0,0 +1,55 @@
1
+
2
+ # A simple class hierarchy declaring a few different types of methods on it that I can write cucumber features against something other than standard ruby classes/methods
3
+
4
+ module Eater
5
+ def eat(*foods)
6
+ #verify varargs
7
+ end
8
+ end
9
+
10
+ class Animal
11
+ end
12
+
13
+ class Dog < Animal
14
+ include Eater
15
+
16
+ # to exercise stub & verify of attributes: matching(regex)
17
+ attr_accessor :name
18
+
19
+ def walk_to(x,y)
20
+ 'use me to exercise numeric matchers: numeric, less_than, greater_than, within_range'
21
+ end
22
+
23
+ def introduce_to(animal)
24
+ 'use me to exercise identity matchers: anything, is_a(Animal), is_a(Cat), any(Animal), any(Dog)'
25
+ end
26
+
27
+ def holler_at(loudly)
28
+ 'use me to exercise: boolean'
29
+ end
30
+
31
+ def use_toys(hash_of_toys_and_actions)
32
+ 'use me to exercise: hash_including'
33
+ end
34
+
35
+ def clean_toys(array_of_toys)
36
+ 'use me to exercise: including'
37
+ end
38
+
39
+ def purebred?
40
+ 'stub me to return a boolean'
41
+ end
42
+
43
+ end
44
+
45
+ class Cat < Animal
46
+ end
47
+
48
+ class Turtle < Animal
49
+ def initialize(shell)
50
+ @shell = shell
51
+ end
52
+
53
+ def swim
54
+ end
55
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH << File.expand_path('../../../lib',__FILE__)
2
+ $LOAD_PATH << File.expand_path('../',__FILE__)
3
+ require 'gimme'
4
+ require 'animals'
@@ -0,0 +1,39 @@
1
+ Feature: Encountering Unknown Methods
2
+
3
+ As a test author
4
+ I want my test double to yell at me when I try invoking a method that instances of the class being doubled wouldn't respond to
5
+ so that I don't find myself with a green bar and a dependency that can't do what the test thinks it does.
6
+
7
+ However, I also want to be able to stub and verify methods that aren't apparently on the class
8
+ so that I can test behavior that I'm confident will be added to the test double's real counterpart dynamically at runtime
9
+
10
+ Scenario: on a classy double, stubbing an unknown method
11
+ Given a new Dog test double
12
+ When I stub meow to return "Woof"
13
+ Then a NoMethodError is raised
14
+
15
+ Scenario: on a classy double, verifying an unknown method
16
+ Given a new Dog test double
17
+ When I invoke gobbeldy_gook
18
+ Then verifying gobbeldy_gook raises a NoMethodError
19
+
20
+ Scenario: on a classy double, stubbing a method with "give!"
21
+ Given a new Dog test double
22
+ When I stub! meow to return :woof
23
+ Then invoking meow returns :woof
24
+
25
+ Scenario: on a classy double, verifying a method with "verify!"
26
+ Given a new Dog test double
27
+ When I invoke meow
28
+ Then I can verify! meow has been invoked 1 time
29
+
30
+ Scenario: on a generic double, stubbing an unknown method
31
+ Given a new test double
32
+ When I stub meow to return "Woof"
33
+ Then no error is raised
34
+
35
+ Scenario: on a generic double, verifying an unknown method
36
+ Given a new test double
37
+ When I invoke gobbeldy_gook
38
+ Then I can verify gobbeldy_gook has been invoked 1 time
39
+
@@ -0,0 +1,21 @@
1
+ Feature: verify with an anything matcher
2
+
3
+ As a test author
4
+ I want to be able to verify an invocation replacing exact arguments with the anything matcher
5
+ so that I'm able to specify only the parameters that matter to me
6
+
7
+ Scenario: a single-argument
8
+ Given a new Dog test double
9
+ Then I can verify holler_at(anything) has been invoked 0 times
10
+ When I invoke holler_at(false)
11
+ Then I can verify holler_at(anything) has been invoked 1 time
12
+ When I invoke holler_at(true)
13
+ Then I can verify holler_at(anything) has been invoked 2 times
14
+
15
+ Scenario: two arguments
16
+ Given a new Dog test double
17
+ Then I can verify walk_to(anything,anything) has been invoked 0 times
18
+ When I invoke walk_to(12.34,943.1)
19
+ Then I can verify walk_to(anything,anything) has been invoked 1 time
20
+ And I can verify walk_to(anything,943.1) has been invoked 1 time
21
+ And I can verify walk_to(12.34,anything) has been invoked 1 time
@@ -0,0 +1,18 @@
1
+ Feature: verification of no-arg methods
2
+
3
+ As a test author
4
+ I want to verify my test double's no-arg method was invoked
5
+ So that I can specify its behavior
6
+
7
+ Scenario:
8
+ Given a new test double
9
+ But I do not invoke to_s
10
+ Then verifying to_s raises a Gimme::Errors::VerificationFailedError
11
+ But I can verify to_s has been invoked 0 times
12
+
13
+ When I invoke to_s
14
+ Then I can verify to_s has been invoked
15
+ And I can verify to_s has been invoked 1 time
16
+
17
+ When I invoke to_s
18
+ Then I can verify to_s has been invoked 2 times
@@ -0,0 +1,21 @@
1
+ Feature: verification of argumentative methods
2
+
3
+ As a test author
4
+ I want to verify my test double's argument-having method was invoked
5
+ So that I can specify the exact arguments
6
+
7
+ Scenario:
8
+ Given a new test double
9
+ But I do not invoke equal?(:pants)
10
+ Then verifying equal?(:pants) raises a Gimme::Errors::VerificationFailedError
11
+ But I can verify equal?(:pants) has been invoked 0 times
12
+
13
+ When I invoke equal?(:pants)
14
+ Then I can verify equal?(:pants) has been invoked
15
+ And I can verify equal?(:pants) has been invoked 1 time
16
+ And I can verify equal?(:kaka) has been invoked 0 times
17
+
18
+ When I invoke equal?(:pants)
19
+ And I invoke equal?(:kaka)
20
+ Then I can verify equal?(:kaka) has been invoked 1 time
21
+ And I can verify equal?(:pants) has been invoked 2 times
data/gimme.gemspec ADDED
@@ -0,0 +1,86 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{gimme}
8
+ s.version = "0.1.6"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Justin Searls"]
12
+ s.date = %q{2011-01-18}
13
+ s.description = %q{gimme attempts to bring to Ruby a test double workflow akin to Mockito in Java. Major distinctions include preserving arrange-act-assert in tests, fast feedback for methods the double's real counterpart may not know how to respond to, no string/symbolic representations of methods, argument captors, and strong opinions (weakly held). }
14
+ s.email = %q{searls@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.markdown",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".bundle/config",
22
+ ".document",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.markdown",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "features/argument_captors.feature",
31
+ "features/gimme_next.feature",
32
+ "features/step_definitions/gimme_steps.rb",
33
+ "features/stub_basic.feature",
34
+ "features/stub_matchers.feature",
35
+ "features/stub_sensible_defaults.feature",
36
+ "features/support/animals.rb",
37
+ "features/support/env.rb",
38
+ "features/unknown_methods.feature",
39
+ "features/verify_matcher_anything.feature",
40
+ "features/verify_no_args.feature",
41
+ "features/verify_with_args.feature",
42
+ "gimme.gemspec",
43
+ "lib/gimme-double.rb",
44
+ "lib/gimme.rb",
45
+ "lib/gimme/captor.rb",
46
+ "lib/gimme/errors.rb",
47
+ "lib/gimme/gives.rb",
48
+ "lib/gimme/matchers.rb",
49
+ "lib/gimme/method_resolver.rb",
50
+ "lib/gimme/test_double.rb",
51
+ "lib/gimme/verifies.rb"
52
+ ]
53
+ s.homepage = %q{http://github.com/searls/gimme}
54
+ s.licenses = ["MIT"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.4.2}
57
+ s.summary = %q{gimme — a low-specification test double library for Ruby}
58
+
59
+ if s.respond_to? :specification_version then
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
63
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
65
+ s.add_development_dependency(%q<rspec>, [">= 1.3.1"])
66
+ s.add_development_dependency(%q<cucumber>, [">= 0.10.0"])
67
+ s.add_development_dependency(%q<rspec>, [">= 1.3.1"])
68
+ s.add_development_dependency(%q<cucumber>, [">= 0.10.0"])
69
+ else
70
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
71
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
72
+ s.add_dependency(%q<rspec>, [">= 1.3.1"])
73
+ s.add_dependency(%q<cucumber>, [">= 0.10.0"])
74
+ s.add_dependency(%q<rspec>, [">= 1.3.1"])
75
+ s.add_dependency(%q<cucumber>, [">= 0.10.0"])
76
+ end
77
+ else
78
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
80
+ s.add_dependency(%q<rspec>, [">= 1.3.1"])
81
+ s.add_dependency(%q<cucumber>, [">= 0.10.0"])
82
+ s.add_dependency(%q<rspec>, [">= 1.3.1"])
83
+ s.add_dependency(%q<cucumber>, [">= 0.10.0"])
84
+ end
85
+ end
86
+
@@ -0,0 +1,24 @@
1
+ module Gimme
2
+
3
+ class Captor
4
+ attr_accessor :value
5
+ end
6
+
7
+ module Matchers
8
+ class Capture < Matchers::Matcher
9
+ def initialize(captor)
10
+ @captor = captor
11
+ end
12
+
13
+ def matches?(arg)
14
+ @captor.value = arg
15
+ true
16
+ end
17
+ end
18
+
19
+ def capture(captor)
20
+ Capture.new(captor)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,6 @@
1
+ module Gimme
2
+ module Errors
3
+ class VerificationFailedError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,27 @@
1
+ module Gimme
2
+
3
+ class Gives < BlankSlate
4
+ attr_accessor :raises_no_method_error
5
+ def initialize(double)
6
+ @double = double
7
+ @raises_no_method_error = true
8
+ end
9
+
10
+ def method_missing(sym, *args, &block)
11
+ sym = MethodResolver.resolve_sent_method(@double,sym,args,@raises_no_method_error)
12
+
13
+ @double.stubbings[sym] ||= {}
14
+ @double.stubbings[sym][args] = block if block
15
+ end
16
+ end
17
+
18
+ def give(double)
19
+ Gimme::Gives.new(double)
20
+ end
21
+
22
+ def give!(double)
23
+ give = give(double)
24
+ give.raises_no_method_error = false
25
+ give
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ module Gimme
2
+ module Matchers
3
+ class Matcher
4
+ def matches?(arg)
5
+ false
6
+ end
7
+ end
8
+
9
+ class Anything < Matcher
10
+ def matches?(arg)
11
+ true
12
+ end
13
+ end
14
+ def anything
15
+ Gimme::Matchers::Anything.new
16
+ end
17
+
18
+ class IsA < Matcher
19
+ def initialize(cls)
20
+ @cls = cls
21
+ end
22
+
23
+ def matches?(arg)
24
+ arg.kind_of?(@cls)
25
+ end
26
+ end
27
+ def is_a(cls)
28
+ Gimme::Matchers::IsA.new(cls)
29
+ end
30
+
31
+ class Any < IsA
32
+ def matches?(arg)
33
+ arg == nil || arg.kind_of?(@cls)
34
+ end
35
+ end
36
+ def any(cls)
37
+ Gimme::Matchers::Any.new(cls)
38
+ end
39
+
40
+ class Numeric < Matcher
41
+ def matches?(arg)
42
+ arg.kind_of?(Fixnum) || arg.kind_of?(Numeric) || arg.kind_of?(Float)
43
+ end
44
+ end
45
+ def numeric
46
+ Gimme::Matchers::Numeric.new
47
+ end
48
+
49
+ class Boolean < Matcher
50
+ def matches?(arg)
51
+ arg.kind_of?(TrueClass) || arg.kind_of?(FalseClass)
52
+ end
53
+ end
54
+ def boolean
55
+ Gimme::Matchers::Boolean.new
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Gimme
2
+
3
+ class MethodResolver
4
+ def self.resolve_sent_method(double,sym,args,raises_no_method_error=true)
5
+ cls = double.cls
6
+ sym = args.shift if sym == :send
7
+ if cls && raises_no_method_error
8
+ if cls.private_methods.include?(sym.to_s)
9
+ raise NoMethodError.new("#{sym} is a private method of your #{cls} test double, so stubbing/verifying it
10
+ might not be a great idea. If you want to try to stub or verify this method anyway, then you can
11
+ invoke give! or verify! to suppress this error.")
12
+ elsif !cls.instance_methods.include?(sym.to_s)
13
+ raise NoMethodError.new("Your test double of #{cls} may not know how to respond to the '#{sym}' method.
14
+ If you're confident that a real #{cls} will know how to respond to '#{sym}', then you can
15
+ invoke give! or verify! to suppress this error.")
16
+ end
17
+ end
18
+
19
+ sym
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,63 @@
1
+ module Gimme
2
+ class BlankSlate
3
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
4
+ end
5
+
6
+ class TestDouble < BlankSlate
7
+ attr_accessor :cls
8
+ attr_accessor :stubbings
9
+ attr_reader :invocations
10
+
11
+ def initialize(cls=nil)
12
+ @cls = cls
13
+ @stubbings = {}
14
+ @invocations = {}
15
+ end
16
+
17
+ def method_missing(sym, *args, &block)
18
+ sym = MethodResolver.resolve_sent_method(self,sym,args,false)
19
+
20
+ @invocations[sym] ||= {}
21
+ @stubbings[sym] ||= {}
22
+
23
+ @invocations[sym][args] = 1 + (@invocations[sym][args]||0)
24
+
25
+ matching_stub_block = nil
26
+ @stubbings[sym].each do |stub_args,stub_block|
27
+ matching = args.size == stub_args.size
28
+ args.each_index do |i|
29
+ unless args[i] == stub_args[i] || (stub_args[i].respond_to?(:matches?) && stub_args[i].matches?(args[i]))
30
+ matching = false
31
+ break
32
+ end
33
+ end
34
+ matching_stub_block = stub_block if matching
35
+ end
36
+
37
+ if matching_stub_block
38
+ matching_stub_block.call
39
+ elsif sym.to_s[-1,1] == '?'
40
+ false
41
+ else
42
+ nil
43
+ end
44
+ end
45
+ end
46
+
47
+ def gimme(cls=nil)
48
+ Gimme::TestDouble.new(cls)
49
+ end
50
+
51
+ def gimme_next(cls)
52
+ double = Gimme::TestDouble.new(cls)
53
+ meta_class = class << cls; self; end
54
+ real_new = cls.method(:new)
55
+ meta_class.send(:define_method,:new) do |*args|
56
+ double.send(:initialize,*args)
57
+ meta_class.send(:define_method,:new,real_new) #restore :new on the class
58
+ double
59
+ end
60
+ double
61
+ end
62
+
63
+ end
@@ -0,0 +1,44 @@
1
+ module Gimme
2
+
3
+ class Verifies < BlankSlate
4
+ attr_accessor :raises_no_method_error
5
+ def initialize(double,times=1)
6
+ @double = double
7
+ @times = times
8
+ @raises_no_method_error = true
9
+ end
10
+
11
+ def method_missing(sym, *args, &block)
12
+ sym = MethodResolver.resolve_sent_method(@double,sym,args,@raises_no_method_error)
13
+
14
+ #gosh, this loop sure looks familiar. just like another ugly loop I know. TODO.
15
+ invoked = 0
16
+ if @double.invocations[sym]
17
+ @double.invocations[sym].each do |invoke_args,count|
18
+ matching = args.size == invoke_args.size
19
+ invoke_args.each_index do |i|
20
+ unless invoke_args[i] == args[i] || (args[i].respond_to?(:matches?) && args[i].matches?(invoke_args[i]))
21
+ matching = false
22
+ break
23
+ end
24
+ end
25
+ invoked += count if matching
26
+ end
27
+ end
28
+
29
+ if invoked != @times
30
+ raise Errors::VerificationFailedError.new("expected #{sym} to have been called with #{args}")
31
+ end
32
+ end
33
+ end
34
+
35
+ def verify(double,times=1)
36
+ Gimme::Verifies.new(double,times)
37
+ end
38
+
39
+ def verify!(double,times=1)
40
+ verify = verify(double,times)
41
+ verify.raises_no_method_error = false
42
+ verify
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ require 'gimme'
data/lib/gimme.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'gimme/test_double'
2
+ require 'gimme/method_resolver'
3
+ require 'gimme/errors'
4
+ require 'gimme/gives'
5
+ require 'gimme/verifies'
6
+ require 'gimme/matchers'
7
+ require 'gimme/captor'
8
+
9
+ include Gimme
10
+ include Gimme::Matchers