gimme 0.1.8 → 0.2.0
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/Gemfile +3 -1
- data/Gemfile.lock +10 -4
- data/README.markdown +41 -41
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/features/step_definitions/gimme_steps.rb +15 -1
- data/features/stub_class_methods.feature +21 -0
- data/features/support/animals.rb +16 -8
- data/gimme.gemspec +22 -6
- data/lib/gimme.rb +7 -3
- data/lib/gimme/dsl.rb +50 -0
- data/lib/gimme/gives.rb +1 -10
- data/lib/gimme/gives_class_methods.rb +54 -0
- data/lib/gimme/invokes_satisfied_stubbing.rb +29 -0
- data/lib/gimme/reset.rb +14 -0
- data/lib/gimme/resolves_methods.rb +40 -0
- data/lib/gimme/rspec_adapter.rb +1 -0
- data/lib/gimme/test_double.rb +2 -36
- data/lib/gimme/verifies.rb +1 -10
- data/spec/gimme/captor_spec.rb +23 -22
- data/spec/gimme/errors_spec.rb +4 -2
- data/spec/gimme/gives_class_methods_spec.rb +51 -0
- data/spec/gimme/gives_spec.rb +25 -38
- data/spec/gimme/matchers_spec.rb +89 -88
- data/spec/gimme/resolves_methods_spec.rb +73 -0
- data/spec/gimme/rspec_adapter_spec.rb +14 -0
- data/spec/gimme/shared_examples/shared_gives_examples.rb +34 -0
- data/spec/gimme/test_double_spec.rb +18 -16
- data/spec/gimme/verifies_spec.rb +92 -91
- metadata +53 -29
- data/lib/gimme/method_resolver.rb +0 -33
data/lib/gimme.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
require 'gimme/test_double'
|
2
|
-
require 'gimme/
|
2
|
+
require 'gimme/resolves_methods'
|
3
3
|
require 'gimme/errors'
|
4
|
+
require 'gimme/gives_class_methods'
|
4
5
|
require 'gimme/gives'
|
5
6
|
require 'gimme/verifies'
|
6
7
|
require 'gimme/matchers'
|
7
8
|
require 'gimme/captor'
|
9
|
+
require 'gimme/invokes_satisfied_stubbing'
|
10
|
+
require 'gimme/reset'
|
11
|
+
require 'gimme/dsl'
|
12
|
+
|
8
13
|
require 'gimme/rspec_adapter'
|
9
14
|
|
10
|
-
include Gimme
|
11
15
|
include Gimme::Matchers
|
12
|
-
|
16
|
+
include Gimme::DSL
|
data/lib/gimme/dsl.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Gimme
|
2
|
+
module DSL
|
3
|
+
|
4
|
+
# Instantiation
|
5
|
+
|
6
|
+
def gimme(cls=nil)
|
7
|
+
Gimme::TestDouble.new(cls)
|
8
|
+
end
|
9
|
+
|
10
|
+
def gimme_next(cls)
|
11
|
+
double = Gimme::TestDouble.new(cls)
|
12
|
+
meta_class = class << cls; self; end
|
13
|
+
real_new = cls.method(:new)
|
14
|
+
meta_class.send(:define_method,:new) do |*args|
|
15
|
+
double.send(:initialize,*args)
|
16
|
+
meta_class.send(:define_method,:new,real_new) #restore :new on the class
|
17
|
+
double
|
18
|
+
end
|
19
|
+
double
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Stubbing
|
24
|
+
def give(double)
|
25
|
+
if double.kind_of? Class
|
26
|
+
Gimme::GivesClassMethods.new(double)
|
27
|
+
else
|
28
|
+
Gimme::Gives.new(double)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def give!(double)
|
33
|
+
give = give(double)
|
34
|
+
give.raises_no_method_error = false
|
35
|
+
give
|
36
|
+
end
|
37
|
+
|
38
|
+
# Verification
|
39
|
+
def verify(double,times=1)
|
40
|
+
Gimme::Verifies.new(double,times)
|
41
|
+
end
|
42
|
+
|
43
|
+
def verify!(double,times=1)
|
44
|
+
verify = verify(double,times)
|
45
|
+
verify.raises_no_method_error = false
|
46
|
+
verify
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/gimme/gives.rb
CHANGED
@@ -8,20 +8,11 @@ module Gimme
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def method_missing(sym, *args, &block)
|
11
|
-
sym =
|
11
|
+
sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
|
12
12
|
|
13
13
|
@double.stubbings[sym] ||= {}
|
14
14
|
@double.stubbings[sym][args] = block if block
|
15
15
|
end
|
16
16
|
end
|
17
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
18
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Gimme
|
2
|
+
|
3
|
+
class GivesClassMethods < BlankSlate
|
4
|
+
attr_accessor :raises_no_method_error
|
5
|
+
def initialize(cls)
|
6
|
+
@cls = cls
|
7
|
+
@raises_no_method_error = true
|
8
|
+
@@stubbings ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
cls = @cls
|
13
|
+
meta_class = meta_for(@cls)
|
14
|
+
method = ResolvesMethods.new(meta_class,method,args).resolve(@raises_no_method_error)
|
15
|
+
hidden_method_name = hidden_name_for(method)
|
16
|
+
|
17
|
+
if @cls.respond_to?(method) && !@cls.respond_to?(hidden_method_name)
|
18
|
+
meta_class.send(:alias_method, hidden_method_name, method)
|
19
|
+
end
|
20
|
+
|
21
|
+
@@stubbings[cls] ||= {}
|
22
|
+
@@stubbings[cls][method] ||= {}
|
23
|
+
@@stubbings[cls][method][args] = block if block
|
24
|
+
|
25
|
+
#TODO this will be redundantly overwritten
|
26
|
+
meta_class.instance_eval do
|
27
|
+
define_method method do |*actual_args|
|
28
|
+
InvokesSatisfiedStubbing.new(@@stubbings[cls]).invoke(method, actual_args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Gimme.on_reset do
|
33
|
+
if cls.respond_to?(hidden_method_name)
|
34
|
+
meta_class.instance_eval { define_method method, cls.method(hidden_method_name) }
|
35
|
+
meta_class.send(:remove_method, hidden_method_name)
|
36
|
+
else
|
37
|
+
meta_class.send(:remove_method, method)
|
38
|
+
end
|
39
|
+
@@stubbings.clear
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def meta_for(cls)
|
46
|
+
(class << cls; self; end)
|
47
|
+
end
|
48
|
+
|
49
|
+
def hidden_name_for(method)
|
50
|
+
"__gimme_#{method}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Gimme
|
2
|
+
class InvokesSatisfiedStubbing
|
3
|
+
def initialize(stubbings)
|
4
|
+
@stubbings = stubbings
|
5
|
+
end
|
6
|
+
|
7
|
+
def invoke(method, args)
|
8
|
+
matching_stub_block = nil
|
9
|
+
@stubbings[method].each do |stub_args,stub_block|
|
10
|
+
matching = args.size == stub_args.size
|
11
|
+
args.each_index do |i|
|
12
|
+
unless args[i] == stub_args[i] || (stub_args[i].respond_to?(:matches?) && stub_args[i].matches?(args[i]))
|
13
|
+
matching = false
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
matching_stub_block = stub_block if matching
|
18
|
+
end
|
19
|
+
|
20
|
+
if matching_stub_block
|
21
|
+
matching_stub_block.call
|
22
|
+
elsif method.to_s[-1,1] == '?'
|
23
|
+
false
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gimme/reset.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Gimme
|
2
|
+
|
3
|
+
class ResolvesMethods
|
4
|
+
def initialize(cls,sym,args)
|
5
|
+
@cls = cls
|
6
|
+
@sym = sym
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
def resolve(raises_no_method_error=true)
|
11
|
+
@sym = @args.shift if @sym == :send
|
12
|
+
if @cls && raises_no_method_error
|
13
|
+
if @cls.private_method_defined?(named(@sym))
|
14
|
+
raise NoMethodError.new("#{@sym} is a private method of your #{@cls} test double, so stubbing/verifying it
|
15
|
+
might not be a great idea. If you want to try to stub or verify this method anyway, then you can
|
16
|
+
invoke give! or verify! to suppress this error.")
|
17
|
+
elsif !@cls.instance_methods.include?(named(@sym))
|
18
|
+
raise NoMethodError.new("Your test double of #{@cls} may not know how to respond to the '#{@sym}' method.
|
19
|
+
If you're confident that a real #{@cls} will know how to respond to '#{@sym}', then you can
|
20
|
+
invoke give! or verify! to suppress this error.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@sym
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
if RUBY_VERSION >= "1.9.2"
|
30
|
+
def named(sym)
|
31
|
+
sym
|
32
|
+
end
|
33
|
+
else
|
34
|
+
def named(sym)
|
35
|
+
sym.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/gimme/rspec_adapter.rb
CHANGED
data/lib/gimme/test_double.rb
CHANGED
@@ -18,49 +18,15 @@ module Gimme
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def method_missing(sym, *args, &block)
|
21
|
-
sym =
|
21
|
+
sym = ResolvesMethods.new(self.cls,sym,args).resolve(false)
|
22
22
|
|
23
23
|
@invocations[sym] ||= {}
|
24
24
|
@stubbings[sym] ||= {}
|
25
25
|
|
26
26
|
@invocations[sym][args] = 1 + (@invocations[sym][args]||0)
|
27
27
|
|
28
|
-
|
29
|
-
@stubbings[sym].each do |stub_args,stub_block|
|
30
|
-
matching = args.size == stub_args.size
|
31
|
-
args.each_index do |i|
|
32
|
-
unless args[i] == stub_args[i] || (stub_args[i].respond_to?(:matches?) && stub_args[i].matches?(args[i]))
|
33
|
-
matching = false
|
34
|
-
break
|
35
|
-
end
|
36
|
-
end
|
37
|
-
matching_stub_block = stub_block if matching
|
38
|
-
end
|
39
|
-
|
40
|
-
if matching_stub_block
|
41
|
-
matching_stub_block.call
|
42
|
-
elsif sym.to_s[-1,1] == '?'
|
43
|
-
false
|
44
|
-
else
|
45
|
-
nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def gimme(cls=nil)
|
51
|
-
Gimme::TestDouble.new(cls)
|
52
|
-
end
|
53
|
-
|
54
|
-
def gimme_next(cls)
|
55
|
-
double = Gimme::TestDouble.new(cls)
|
56
|
-
meta_class = class << cls; self; end
|
57
|
-
real_new = cls.method(:new)
|
58
|
-
meta_class.send(:define_method,:new) do |*args|
|
59
|
-
double.send(:initialize,*args)
|
60
|
-
meta_class.send(:define_method,:new,real_new) #restore :new on the class
|
61
|
-
double
|
28
|
+
InvokesSatisfiedStubbing.new(@stubbings).invoke(sym, args)
|
62
29
|
end
|
63
|
-
double
|
64
30
|
end
|
65
31
|
|
66
32
|
end
|
data/lib/gimme/verifies.rb
CHANGED
@@ -9,7 +9,7 @@ module Gimme
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def method_missing(sym, *args, &block)
|
12
|
-
sym =
|
12
|
+
sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
|
13
13
|
|
14
14
|
#gosh, this loop sure looks familiar. just like another ugly loop I know. TODO.
|
15
15
|
invoked = 0
|
@@ -41,13 +41,4 @@ module Gimme
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def verify(double,times=1)
|
45
|
-
Gimme::Verifies.new(double,times)
|
46
|
-
end
|
47
|
-
|
48
|
-
def verify!(double,times=1)
|
49
|
-
verify = verify(double,times)
|
50
|
-
verify.raises_no_method_error = false
|
51
|
-
verify
|
52
|
-
end
|
53
44
|
end
|
data/spec/gimme/captor_spec.rb
CHANGED
@@ -1,34 +1,35 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
describe
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module Gimme
|
4
|
+
describe Captor do
|
5
|
+
describe "#value" do
|
6
|
+
Given(:captor) { Captor.new }
|
7
|
+
When { captor.value = "panda" }
|
8
|
+
Then { captor.value.should == "panda" }
|
9
|
+
end
|
9
10
|
end
|
10
|
-
end
|
11
11
|
|
12
|
-
describe
|
13
|
-
|
12
|
+
describe Matchers::Capture do
|
13
|
+
Given(:captor) { Captor.new }
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
shared_examples_for "an argument captor" do
|
16
|
+
describe "#matches?" do
|
17
|
+
When(:result) { a_capture.matches?("anything at all") }
|
18
|
+
Then { result.should == true }
|
19
|
+
Then { captor.value.should == "anything at all" }
|
20
|
+
end
|
20
21
|
end
|
21
|
-
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
context "using the class API" do
|
24
|
+
it_behaves_like "an argument captor" do
|
25
|
+
Given(:a_capture) { Capture.new(captor) }
|
26
|
+
end
|
26
27
|
end
|
27
|
-
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
context "using gimme DSL" do
|
30
|
+
it_behaves_like "an argument captor" do
|
31
|
+
Given(:a_capture) { capture(captor) }
|
32
|
+
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|
data/spec/gimme/errors_spec.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'gimme/shared_examples/shared_gives_examples'
|
3
|
+
|
4
|
+
module Gimme
|
5
|
+
describe GivesClassMethods do
|
6
|
+
|
7
|
+
class Bunny
|
8
|
+
def self.nibble
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.eat(food)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Rabbit
|
16
|
+
def self.eat(food)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "two classes with similar stubbings" do
|
21
|
+
Given { give(Rabbit).eat("carrot") { "yum" } }
|
22
|
+
Given { give(Bunny).eat("carrot") { "yay" } }
|
23
|
+
Then { Rabbit.eat("carrot").should == "yum" }
|
24
|
+
Then { Bunny.eat("carrot").should == "yay" }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "using the class API" do
|
28
|
+
Given(:subject) { Bunny }
|
29
|
+
|
30
|
+
it_behaves_like "a normal stubbing" do
|
31
|
+
Given(:gives) { GivesClassMethods.new(subject) }
|
32
|
+
end
|
33
|
+
|
34
|
+
it_behaves_like "an overridden stubbing" do
|
35
|
+
Given(:gives) { GivesClassMethods.new(subject) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "using the gimme DSL" do
|
40
|
+
Given(:subject) { Bunny }
|
41
|
+
|
42
|
+
it_behaves_like "a normal stubbing" do
|
43
|
+
Given(:gives) { give(subject) }
|
44
|
+
end
|
45
|
+
|
46
|
+
it_behaves_like "an overridden stubbing" do
|
47
|
+
Given(:gives) { give!(subject) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/spec/gimme/gives_spec.rb
CHANGED
@@ -1,52 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'gimme/shared_examples/shared_gives_examples'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
def nibble
|
6
|
-
end
|
7
|
-
end
|
4
|
+
module Gimme
|
5
|
+
describe Gives do
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
Then { subject.nibble.should == "nom" }
|
13
|
-
end
|
14
|
-
|
15
|
-
context "stubbing a non-existent method" do
|
16
|
-
When(:stubbing) { lambda { gives.bark { "woof" } } }
|
17
|
-
Then { stubbing.should raise_error NoMethodError }
|
18
|
-
end
|
19
|
-
end
|
7
|
+
class Bunny
|
8
|
+
def nibble
|
9
|
+
end
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
Given { gives.raises_no_method_error =false }
|
24
|
-
When { gives.bark { "woof" } }
|
25
|
-
Then { subject.bark.should == "woof" }
|
11
|
+
def eat(food)
|
12
|
+
end
|
26
13
|
end
|
27
|
-
end
|
28
14
|
|
29
|
-
|
30
|
-
|
15
|
+
context "using the class API" do
|
16
|
+
Given(:subject) { TestDouble.new(Bunny) }
|
31
17
|
|
32
|
-
|
33
|
-
|
34
|
-
|
18
|
+
it_behaves_like "a normal stubbing" do
|
19
|
+
Given(:gives) { Gives.new(subject) }
|
20
|
+
end
|
35
21
|
|
36
|
-
|
37
|
-
|
22
|
+
it_behaves_like "an overridden stubbing" do
|
23
|
+
Given(:gives) { Gives.new(subject) }
|
24
|
+
end
|
38
25
|
end
|
39
|
-
end
|
40
26
|
|
41
|
-
|
42
|
-
|
27
|
+
context "using the gimme DSL" do
|
28
|
+
Given(:subject) { gimme(Bunny) }
|
43
29
|
|
44
|
-
|
45
|
-
|
46
|
-
|
30
|
+
it_behaves_like "a normal stubbing" do
|
31
|
+
Given(:gives) { give(subject) }
|
32
|
+
end
|
47
33
|
|
48
|
-
|
49
|
-
|
34
|
+
it_behaves_like "an overridden stubbing" do
|
35
|
+
Given(:gives) { give!(subject) }
|
36
|
+
end
|
50
37
|
end
|
51
38
|
end
|
52
|
-
end
|
39
|
+
end
|