gimme 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +3 -2
  2. data/Guardfile +1 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.markdown +1 -1
  5. data/VERSION +1 -1
  6. data/features/basics.feature +66 -0
  7. data/features/matchers.feature +126 -0
  8. data/features/{argument_captors.feature → old/argument_captors.feature} +0 -0
  9. data/features/{gimme_next.feature → old/gimme_next.feature} +0 -0
  10. data/features/{stub_basic.feature → old/stub_basic.feature} +0 -0
  11. data/features/{stub_class_methods.feature → old/stub_class_methods.feature} +0 -1
  12. data/features/{stub_matchers.feature → old/stub_matchers.feature} +0 -0
  13. data/features/{stub_sensible_defaults.feature → old/stub_sensible_defaults.feature} +0 -0
  14. data/features/{unknown_methods.feature → old/unknown_methods.feature} +0 -0
  15. data/features/{verify_matcher_anything.feature → old/verify_matcher_anything.feature} +0 -0
  16. data/features/old/verify_no_args.feature +23 -0
  17. data/features/{verify_with_args.feature → old/verify_with_args.feature} +0 -0
  18. data/features/readme.md +3 -0
  19. data/features/step_definitions/doc_steps.rb +12 -0
  20. data/features/step_definitions/gimme_steps.rb +17 -0
  21. data/features/support/animals.rb +9 -0
  22. data/gimme.gemspec +35 -18
  23. data/lib/gimme.rb +8 -1
  24. data/lib/gimme/dsl.rb +19 -5
  25. data/lib/gimme/ensures_class_method_restoration.rb +18 -0
  26. data/lib/gimme/gives.rb +1 -2
  27. data/lib/gimme/gives_class_methods.rb +5 -29
  28. data/lib/gimme/invocation_store.rb +14 -0
  29. data/lib/gimme/invokes_satisfied_stubbing.rb +18 -10
  30. data/lib/gimme/method_store.rb +21 -0
  31. data/lib/gimme/reset.rb +10 -3
  32. data/lib/gimme/resolves_methods.rb +1 -1
  33. data/lib/gimme/spies_on_class_methods.rb +28 -0
  34. data/lib/gimme/store.rb +38 -0
  35. data/lib/gimme/stubbing_store.rb +17 -0
  36. data/lib/gimme/test_double.rb +13 -10
  37. data/lib/gimme/verifies.rb +10 -6
  38. data/lib/gimme/verifies_class_methods.rb +8 -0
  39. data/spec/gimme/shared_examples/shared_gives_examples.rb +28 -26
  40. data/spec/gimme/shared_examples/shared_verifies_examples.rb +91 -0
  41. data/spec/gimme/spies_on_class_method_spec.rb +58 -0
  42. data/spec/gimme/verifies_class_methods_spec.rb +46 -0
  43. data/spec/gimme/verifies_spec.rb +7 -94
  44. metadata +59 -42
  45. data/features/verify_no_args.feature +0 -18
@@ -1,14 +1,21 @@
1
1
  require 'gimme/test_double'
2
2
  require 'gimme/resolves_methods'
3
3
  require 'gimme/errors'
4
- require 'gimme/gives_class_methods'
5
4
  require 'gimme/gives'
5
+ require 'gimme/gives_class_methods'
6
+ require 'gimme/ensures_class_method_restoration'
6
7
  require 'gimme/verifies'
8
+ require 'gimme/spies_on_class_methods'
9
+ require 'gimme/verifies_class_methods'
7
10
  require 'gimme/matchers'
8
11
  require 'gimme/captor'
9
12
  require 'gimme/invokes_satisfied_stubbing'
10
13
  require 'gimme/reset'
11
14
  require 'gimme/dsl'
15
+ require 'gimme/store'
16
+ require 'gimme/stubbing_store'
17
+ require 'gimme/invocation_store'
18
+ require 'gimme/method_store'
12
19
 
13
20
  require 'gimme/rspec_adapter'
14
21
 
@@ -1,8 +1,8 @@
1
1
  module Gimme
2
2
  module DSL
3
-
3
+
4
4
  # Instantiation
5
-
5
+
6
6
  def gimme(cls=nil)
7
7
  Gimme::TestDouble.new(cls)
8
8
  end
@@ -19,7 +19,7 @@ module Gimme
19
19
  double
20
20
  end
21
21
 
22
-
22
+
23
23
  # Stubbing
24
24
  def give(double)
25
25
  if double.kind_of? Class
@@ -37,7 +37,11 @@ module Gimme
37
37
 
38
38
  # Verification
39
39
  def verify(double,times=1)
40
- Gimme::Verifies.new(double,times)
40
+ if double.kind_of? Class
41
+ Gimme::VerifiesClassMethods.new(double, times)
42
+ else
43
+ Gimme::Verifies.new(double,times)
44
+ end
41
45
  end
42
46
 
43
47
  def verify!(double,times=1)
@@ -45,6 +49,16 @@ module Gimme
45
49
  verify.raises_no_method_error = false
46
50
  verify
47
51
  end
48
-
52
+
53
+ # Spying on class methods
54
+ def spy_on(cls, method)
55
+ SpiesOnClassMethod.new(cls).spy(method)
56
+ end
57
+
58
+ def spy_on!(cls, method)
59
+ spies_on = SpiesOnClassMethod.new(cls)
60
+ spies_on.raises_no_method_error = false
61
+ spies_on.spy(method)
62
+ end
49
63
  end
50
64
  end
@@ -0,0 +1,18 @@
1
+ module Gimme
2
+ class EnsuresClassMethodRestoration
3
+ def initialize(cls)
4
+ @cls = cls
5
+ end
6
+
7
+ def ensure(method)
8
+ meta_class = (class << @cls; self; end)
9
+ Gimme.on_reset do
10
+ if real_method = Gimme.class_methods.get(@cls, method)
11
+ meta_class.instance_eval { define_method method, real_method }
12
+ else
13
+ meta_class.send(:remove_method, method)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -10,8 +10,7 @@ module Gimme
10
10
  def method_missing(sym, *args, &block)
11
11
  sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
12
12
 
13
- @double.stubbings[sym] ||= {}
14
- @double.stubbings[sym][args] = block if block
13
+ Gimme.stubbings.set(@double, sym, args, block)
15
14
  end
16
15
  end
17
16
 
@@ -5,50 +5,26 @@ module Gimme
5
5
  def initialize(cls)
6
6
  @cls = cls
7
7
  @raises_no_method_error = true
8
- @@stubbings ||= {}
9
8
  end
10
9
 
11
10
  def method_missing(method, *args, &block)
12
11
  cls = @cls
13
- meta_class = meta_for(@cls)
12
+ meta_class = (class << cls; self; end)
14
13
  method = ResolvesMethods.new(meta_class,method,args).resolve(@raises_no_method_error)
15
- hidden_method_name = hidden_name_for(method)
16
14
 
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
15
+ Gimme.class_methods.set(cls, method)
16
+ Gimme.stubbings.set(cls, method, args, block)
24
17
 
25
18
  #TODO this will be redundantly overwritten
26
19
  meta_class.instance_eval do
27
20
  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)
21
+ InvokesSatisfiedStubbing.new(Gimme.stubbings.get(cls)).invoke(method, actual_args)
38
22
  end
39
- @@stubbings.clear
40
23
  end
41
- end
42
24
 
43
- private
44
-
45
- def meta_for(cls)
46
- (class << cls; self; end)
25
+ EnsuresClassMethodRestoration.new(@cls).ensure(method)
47
26
  end
48
27
 
49
- def hidden_name_for(method)
50
- "__gimme_#{method}"
51
- end
52
28
  end
53
29
 
54
30
  end
@@ -0,0 +1,14 @@
1
+ module Gimme
2
+
3
+ class InvocationStore < Store
4
+
5
+ def increment(double, method, args)
6
+ set(double, method, args, get(double, method, args).to_i.succ)
7
+ end
8
+ end
9
+
10
+ def self.invocations
11
+ @@invocations ||= InvocationStore.new
12
+ end
13
+
14
+ end
@@ -1,11 +1,24 @@
1
1
  module Gimme
2
2
  class InvokesSatisfiedStubbing
3
3
  def initialize(stubbings)
4
- @stubbings = stubbings
4
+ @stubbings = stubbings || {}
5
5
  end
6
6
 
7
7
  def invoke(method, args)
8
- matching_stub_block = nil
8
+ if @stubbings[method] && blk = find_matching_stubbing(method, args)
9
+ blk.call
10
+ elsif method.to_s[-1,1] == '?'
11
+ false
12
+ else
13
+ nil
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def find_matching_stubbing(method, args)
20
+ match = nil
21
+
9
22
  @stubbings[method].each do |stub_args,stub_block|
10
23
  matching = args.size == stub_args.size
11
24
  args.each_index do |i|
@@ -14,16 +27,11 @@ module Gimme
14
27
  break
15
28
  end
16
29
  end
17
- matching_stub_block = stub_block if matching
30
+ match = stub_block if matching
18
31
  end
19
32
 
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
33
+ match
27
34
  end
35
+
28
36
  end
29
37
  end
@@ -0,0 +1,21 @@
1
+ module Gimme
2
+
3
+ class ClassMethodStore < Store
4
+
5
+ def set(cls, method_name)
6
+ id = cls.__id__
7
+ meta = (class << cls; self; end)
8
+ @store[id] ||= {}
9
+
10
+ if meta.method_defined?(method_name) && !@store[id][method_name]
11
+ @store[id][method_name] = cls.method(method_name)
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ def self.class_methods
18
+ @@class_methods ||= ClassMethodStore.new
19
+ end
20
+
21
+ end
@@ -1,14 +1,21 @@
1
1
  module Gimme
2
2
  @@stuff_to_do_on_reset = []
3
+ @@stuff_to_do_on_every_reset = []
3
4
 
4
- def self.on_reset (&blk)
5
- @@stuff_to_do_on_reset << blk
5
+ def self.on_reset (situation = :once, &blk)
6
+ if situation == :once
7
+ @@stuff_to_do_on_reset << blk
8
+ else
9
+ @@stuff_to_do_on_every_reset << blk
10
+ end
6
11
  end
7
12
 
8
13
  def self.reset
9
14
  @@stuff_to_do_on_reset.delete_if do |stuff|
10
15
  stuff.call
11
- true
16
+ end
17
+ @@stuff_to_do_on_every_reset.each do |stuff|
18
+ stuff.call
12
19
  end
13
20
  end
14
21
  end
@@ -1,7 +1,7 @@
1
1
  module Gimme
2
2
 
3
3
  class ResolvesMethods
4
- def initialize(cls,sym,args)
4
+ def initialize(cls, sym, args=[])
5
5
  @cls = cls
6
6
  @sym = sym
7
7
  @args = args
@@ -0,0 +1,28 @@
1
+ module Gimme
2
+ class SpiesOnClassMethod
3
+ attr_accessor :raises_no_method_error
4
+ def initialize(cls)
5
+ @cls = cls
6
+ @raises_no_method_error = true
7
+ end
8
+
9
+ def spy(method)
10
+ meta_class = (class << @cls; self; end)
11
+ method = ResolvesMethods.new(meta_class, method).resolve(@raises_no_method_error)
12
+
13
+ if meta_class.method_defined? method
14
+ Gimme.class_methods.set(@cls, method)
15
+ meta_class.send(:remove_method, method)
16
+ end
17
+
18
+ cls = @cls
19
+ meta_class.instance_eval do
20
+ define_method method do |*args|
21
+ Gimme.invocations.increment(cls, method, args)
22
+ end
23
+ end
24
+
25
+ EnsuresClassMethodRestoration.new(@cls).ensure(method)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ module Gimme
2
+ class Store
3
+ @store = {}
4
+
5
+ def initialize
6
+ @store = {}
7
+ Gimme.on_reset(:each) do
8
+ @store.clear
9
+ end
10
+ end
11
+
12
+ def reset
13
+ @store = {}
14
+ end
15
+
16
+ def set(double, method, args, content)
17
+ id = double.__id__
18
+ @store[id] ||= {}
19
+ @store[id][method] ||= {}
20
+ @store[id][method][args] = content
21
+ end
22
+
23
+ def get(double, method=nil, args=nil)
24
+ id = double.__id__
25
+ if !method
26
+ @store[id]
27
+ elsif @store[id] && !args
28
+ @store[id][method]
29
+ elsif @store[id] && @store[id][method]
30
+ @store[id][method][args]
31
+ end
32
+ end
33
+
34
+ def clear
35
+ @store.clear
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Gimme
2
+
3
+ class StubbingStore < Store
4
+
5
+ def invoke(double, method, args)
6
+ if stubbing = get(double, method, args)
7
+ stubbing.call
8
+ end
9
+ end
10
+
11
+ end
12
+
13
+ def self.stubbings
14
+ @@stubbings ||= StubbingStore.new
15
+ end
16
+
17
+ end
@@ -8,24 +8,27 @@ module Gimme
8
8
 
9
9
  class TestDouble < BlankSlate
10
10
  attr_accessor :cls
11
- attr_accessor :stubbings
12
- attr_reader :invocations
13
11
 
14
12
  def initialize(cls=nil)
15
13
  @cls = cls
16
- @stubbings = {}
17
- @invocations = {}
18
14
  end
19
15
 
20
- def method_missing(sym, *args, &block)
21
- sym = ResolvesMethods.new(self.cls,sym,args).resolve(false)
16
+ def method_missing(method, *args, &block)
17
+ method = ResolvesMethods.new(self.cls, method, args).resolve(false)
18
+ Gimme.invocations.increment(self, method, args)
19
+ InvokesSatisfiedStubbing.new(Gimme.stubbings.get(self)).invoke(method, args)
20
+ end
22
21
 
23
- @invocations[sym] ||= {}
24
- @stubbings[sym] ||= {}
22
+ def hash
23
+ __id__
24
+ end
25
25
 
26
- @invocations[sym][args] = 1 + (@invocations[sym][args]||0)
26
+ def eql?(other)
27
+ __id__ == other.__id__
28
+ end
27
29
 
28
- InvokesSatisfiedStubbing.new(@stubbings).invoke(sym, args)
30
+ def ==(other)
31
+ eql?(other)
29
32
  end
30
33
  end
31
34
 
@@ -8,13 +8,17 @@ module Gimme
8
8
  @raises_no_method_error = true
9
9
  end
10
10
 
11
+ def __gimme__cls
12
+ @double.cls
13
+ end
14
+
11
15
  def method_missing(sym, *args, &block)
12
- sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
16
+ sym = ResolvesMethods.new(__gimme__cls,sym,args).resolve(@raises_no_method_error)
13
17
 
14
18
  #gosh, this loop sure looks familiar. just like another ugly loop I know. TODO.
15
19
  invoked = 0
16
- if @double.invocations[sym]
17
- @double.invocations[sym].each do |invoke_args,count|
20
+ if Gimme.invocations.get(@double, sym)
21
+ Gimme.invocations.get(@double, sym).each do |invoke_args,count|
18
22
  matching = args.size == invoke_args.size
19
23
  invoke_args.each_index do |i|
20
24
  unless invoke_args[i] == args[i] || (args[i].respond_to?(:matches?) && args[i].matches?(invoke_args[i]))
@@ -27,11 +31,11 @@ module Gimme
27
31
  end
28
32
 
29
33
  if invoked != @times
30
- msg = "expected #{@double.cls.to_s}##{sym} to have been called with arguments #{args}"
31
- if !@double.invocations[sym] || @double.invocations[sym].empty?
34
+ msg = "expected #{__gimme__cls.to_s}##{sym} to have been called with arguments #{args}"
35
+ if !Gimme.invocations.get(@double, sym) || Gimme.invocations.get(@double, sym).empty?
32
36
  msg << "\n but was never called"
33
37
  else
34
- msg = @double.invocations[sym].inject msg do |memo, actual|
38
+ msg = Gimme.invocations.get(@double, sym).inject msg do |memo, actual|
35
39
  memo + "\n was actually called #{actual[1]} times with arguments #{actual[0]}"
36
40
  end
37
41
  end
@@ -0,0 +1,8 @@
1
+ module Gimme
2
+
3
+ class VerifiesClassMethods < Verifies
4
+ def __gimme__cls
5
+ (class << @double; self; end)
6
+ end
7
+ end
8
+ end