gimme 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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