muack 0.5.2 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0cc50c953e21826fe85b70e03238b3c22486195
4
- data.tar.gz: 621fad09a9fb9fe21edd940d33e60d0415c12c5f
3
+ metadata.gz: f7727020cca0c41dfd846c0b7628aec71c2cbb59
4
+ data.tar.gz: 9fb966eb9e9e1dc65d8f0e938d2d0a619813f204
5
5
  SHA512:
6
- metadata.gz: f9453f77aeadc5efcecfd33e6785a7ec4d8700c01e72367910f6b68be0480dc2e3699f87c5658c2ce41601ad78b3f3ca2800ba78aa4ccde9d8ee6ea8234e14a8
7
- data.tar.gz: 0d69e0c4d3c544ef7e69c2b60691f369086cac70f2284167dadfc03613087fa2bd78476675ddf9f8504a811eca1abbeb625312082b33f043bb241ccf8928ba41
6
+ metadata.gz: 669fbb6e488468cf698bf28782e989f70a7fa82c447b91ef4efdc62892add2bf828d9604d682fbe4b13780cf1599017fbfff22f5d7fa8bbb43c0603b7e5b5d97
7
+ data.tar.gz: b6465725ab6109aeead47a3bba111df9095434ec557017ad0141c1ff441e51054504f4217d19fc6b9e1ad2ef2e86e6fdd8cc59cc3568d2498b89bdd30e257dd4
data/CHANGES.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # CHANGES
2
2
 
3
+ ## Muack 0.7.0 -- 2013-06-27
4
+
5
+ ### Incompatible changes
6
+
7
+ * Now instead of using mock_proxy, we use `proxy` as a modifier. That says
8
+ mock_proxy and stub_proxy no longer existed. like this:
9
+
10
+ ``` ruby
11
+ mock(object).to_s.proxy
12
+ stub(object).to_s.proxy
13
+ ```
14
+
15
+ We change this due to the introduction of spies.
16
+
17
+ ### Enhancement
18
+
19
+ * We have spies support now. Here's an example:
20
+
21
+ ``` ruby
22
+ subject = Object.new
23
+ stub(subject).foo(1)
24
+ subject.foo(1)
25
+
26
+ spy(subject).foo(1)
27
+ spy(subject).bar # This doesn't verify immediately.
28
+ Muack.verify # This fails, saying `bar` was never called.
29
+ ```
30
+
31
+ * It would now raise a `StubHasNoTimes` exception if you tried to set times
32
+ on stubs, which has no meanings in Muack. Use `mock` or `spy` instead if
33
+ you need to specify times.
34
+
35
+ * Muack.reset and Muack.verify is now thread-safe.
36
+ You can run test cases concurrently now.
37
+
38
+ * AnyInstanceOf now has a more readable inspect.
39
+ * Improved various error messages. e.g. CannotFindInjectionName.
40
+ * You can now set ENV['MUACK_RECURSION_LEVEL'] to raise the limit
41
+ to find a new method name when we're injecting a method. Normally
42
+ this should not happen, and it could be a bug in Muack. But instead of
43
+ putting a magic number 9 out there as before, this might be better.
44
+
3
45
  ## Muack 0.5.2 -- 2013-06-26
4
46
 
5
47
  * Add `returns` modifier which you can pass the return values if passing
data/README.md CHANGED
@@ -101,27 +101,47 @@ User.find('42').times(0)
101
101
  User.find('42') # raises a Muack::Unexpected
102
102
  ```
103
103
 
104
- #### [mock_proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
104
+ #### [proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
105
105
 
106
- Since I don't see how we gain from calling it mock.proxy, in Muack we
107
- just call it `mock_proxy`.
106
+ Instead of calling `proxy` immediately after calling `mock`, we put
107
+ `proxy` the last because it's a method from `Muack::Modifier`.
108
108
 
109
109
  ``` ruby
110
110
  view = controller.template
111
- mock_proxy(view).render(:partial => "right_navigation")
112
- mock_proxy(view).render(:partial => "user_info") do |html|
111
+ mock(view).render(:partial => "right_navigation").proxy
112
+ mock(view).render(:partial => "user_info") do |html|
113
+ html.should include("John Doe")
114
+ "Different html"
115
+ end.proxy
116
+ ```
117
+
118
+ If you feel it is weird to put proxy the last, you can also use
119
+ `returns` modifier to put the block last as this:
120
+
121
+ ``` ruby
122
+ view = controller.template
123
+ mock(view).render(:partial => "right_navigation").proxy
124
+ mock(view).render(:partial => "user_info").proxy.returns do |html|
113
125
  html.should include("John Doe")
114
126
  "Different html"
115
127
  end
116
128
  ```
117
129
 
118
- #### [stub_proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stubproxy)
130
+ The same goes to `stub`.
119
131
 
120
- The same goes to `stub_proxy`.
132
+ ``` ruby
133
+ view = controller.template
134
+ stub(view).render(:partial => "user_info") do |html|
135
+ html.should include("Joe Smith")
136
+ html
137
+ end.proxy
138
+ ```
139
+
140
+ Or use `returns`:
121
141
 
122
142
  ``` ruby
123
143
  view = controller.template
124
- stub_proxy(view).render(:partial => "user_info") do |html|
144
+ stub(view).render(:partial => "user_info").proxy.returns do |html|
125
145
  html.should include("Joe Smith")
126
146
  html
127
147
  end
@@ -141,6 +161,27 @@ any_instance_of(User) do |u|
141
161
  end
142
162
  ```
143
163
 
164
+ #### [Spies](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#spies)
165
+
166
+ We don't try to provide different methods for different testing framework,
167
+ so that we don't have to create so many testing framework adapters, and
168
+ try to be smart to find the correct adapter. There are simply too many
169
+ testing frameworks out there. Ruby's built-in test/unit and minitest have
170
+ a lot of different versions, so does rspec.
171
+
172
+ Here we just try to do it the Muack's way:
173
+
174
+
175
+ ``` ruby
176
+ subject = Object.new
177
+ stub(subject).foo(1)
178
+ subject.foo(1)
179
+
180
+ spy(subject).foo(1)
181
+ spy(subject).bar # This doesn't verify immediately.
182
+ Muack.verify # This fails, saying `bar` was never called.
183
+ ```
184
+
144
185
  #### [Block form](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#block-syntax)
145
186
 
146
187
  Block form is also supported. However we don't support `instance_eval` form.
data/lib/muack.rb CHANGED
@@ -10,12 +10,13 @@ module Muack
10
10
  end
11
11
 
12
12
  def self.session
13
- @session ||= Muack::Session.new
13
+ Thread.current[:muack_session] ||= Muack::Session.new
14
14
  end
15
15
 
16
16
  def self.reset
17
- @session && @session.reset
18
- @session = nil
17
+ session = Thread.current[:muack_session]
18
+ session && session.reset
19
+ Thread.current[:muack_session] = nil
19
20
  end
20
21
 
21
22
  module API
@@ -30,13 +31,8 @@ module Muack
30
31
  if block_given? then yield(ret) else ret end
31
32
  end
32
33
 
33
- def mock_proxy obj=Object.new
34
- ret = Muack.session.mock_proxy(obj)
35
- if block_given? then yield(ret) else ret end
36
- end
37
-
38
- def stub_proxy obj=Object.new
39
- ret = Muack.session.stub_proxy(obj)
34
+ def spy obj
35
+ ret = Muack.session.spy(obj)
40
36
  if block_given? then yield(ret) else ret end
41
37
  end
42
38
 
@@ -1,4 +1,8 @@
1
1
 
2
2
  module Muack
3
- AnyInstanceOf = Class.new(Struct.new(:singleton_class))
3
+ class AnyInstanceOf < Struct.new(:singleton_class)
4
+ def inspect
5
+ "Muack::API.any_instance_of(#{singleton_class})"
6
+ end
7
+ end
4
8
  end
@@ -1,5 +1,6 @@
1
1
 
2
2
  module Muack
3
- Definition = Class.new(Struct.new(:msg, :args, :block, :original_method))
3
+ Definition = Class.new(Struct.new(:msg, :args, :block,
4
+ :original_method, :proxy))
4
5
  WithAnyArgs = Object.new
5
6
  end
@@ -0,0 +1,18 @@
1
+
2
+ module Muack
3
+ Error = Class.new(NotImplementedError)
4
+ class CannotFindInjectionName < Error
5
+ def initialize t, msg
6
+ super "\nCan't find a new method name for :#{msg}, tried #{t} times." \
7
+ "\nSet ENV['MUACK_RECURSION_LEVEL'] to raise this limit."
8
+ end
9
+ end
10
+
11
+ class StubHasNoTimes < Error
12
+ def initialize obj, defi, times
13
+ super "\nUse mocks if you want to specify times.\ne.g. " \
14
+ "mock(#{obj.inspect}).#{defi.msg}(#{defi.args.join(', ')})" \
15
+ ".times(#{times})"
16
+ end
17
+ end
18
+ end
data/lib/muack/mock.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  require 'muack/definition'
3
3
  require 'muack/modifier'
4
4
  require 'muack/failure'
5
+ require 'muack/error'
5
6
 
6
7
  module Muack
7
8
  class Mock < BasicObject
@@ -16,7 +17,7 @@ module Muack
16
17
 
17
18
  # Public API: Bacon needs this, or we often ended up with stack overflow
18
19
  def inspect
19
- "#<#{class << self; self; end.superclass} object=#{object.inspect}>"
20
+ "Muack::API.#{__mock_class.name[/\w+$/].downcase}(#{object.inspect})"
20
21
  end
21
22
 
22
23
  # Public API: Define mocked method
@@ -41,12 +42,17 @@ module Muack
41
42
  __mock_defis[defi.msg].pop
42
43
  end
43
44
 
45
+ # used for Muack::Modifier#times to determine if it's a mock or not
46
+ def __mock_class
47
+ (class << self; self; end).superclass
48
+ end
49
+
44
50
  # used for mocked object to dispatch mocked method
45
- def __mock_dispatch msg, actual_args, actual_block
51
+ def __mock_dispatch msg, actual_args
46
52
  if defi = __mock_defis[msg].shift
47
53
  __mock_disps_push(defi)
48
54
  if __mock_check_args(defi.args, actual_args)
49
- __mock_block_call(defi, actual_args, actual_block)
55
+ defi
50
56
  else
51
57
  Mock.__send__(:raise, # Wrong argument
52
58
  Unexpected.new(object, [defi], msg, actual_args))
@@ -63,6 +69,25 @@ module Muack
63
69
  end
64
70
  end
65
71
 
72
+ # used for mocked object to dispatch mocked method
73
+ def __mock_dispatch_call disp, actual_args, actual_block
74
+ if disp.proxy
75
+ value = yield # need the original context for proxy or AnyInstanceOf
76
+ if disp.block
77
+ disp.block.call(value)
78
+ else
79
+ value
80
+ end
81
+ elsif block = disp.block
82
+ arity = block.arity
83
+ if arity < 0
84
+ block.call(*actual_args , &actual_block)
85
+ else
86
+ block.call(*actual_args.first(arity), &actual_block)
87
+ end
88
+ end
89
+ end
90
+
66
91
  # used for Muack::Session#verify
67
92
  def __mock_verify
68
93
  __mock_defis.values.all?(&:empty?) || begin
@@ -94,7 +119,7 @@ module Muack
94
119
  private
95
120
  def __mock_inject_method defi
96
121
  __mock_injected[defi.msg] = defi
97
- target = object.singleton_class
122
+ target = object.singleton_class # would be the class in AnyInstanceOf
98
123
  Mock.store_original_method(target, defi)
99
124
  __mock_inject_mock_method(target, defi)
100
125
  end
@@ -108,8 +133,9 @@ module Muack
108
133
  end
109
134
 
110
135
  def self.find_new_name klass, message, level=0
111
- raise "Cannot find a suitable method name, tried #{level+1} times." if
112
- level >= 9
136
+ if level >= (::ENV['MUACK_RECURSION_LEVEL'] || 9).to_i
137
+ raise CannotFindInjectionName.new(level+1, message)
138
+ end
113
139
 
114
140
  new_name = "__muack_mock_#{level}_#{message}".to_sym
115
141
  if klass.instance_methods(false).include?(new_name)
@@ -122,21 +148,17 @@ module Muack
122
148
  def __mock_inject_mock_method target, defi
123
149
  mock = self # remember the context
124
150
  target.__send__(:define_method, defi.msg){|*actual_args, &actual_block|
125
- mock.__mock_dispatch(defi.msg, actual_args, actual_block)
151
+ disp = mock.__mock_dispatch(defi.msg, actual_args)
152
+ mock.__mock_dispatch_call(disp, actual_args, actual_block){
153
+ if disp.original_method
154
+ __send__(disp.original_method, *actual_args, &actual_block)
155
+ else
156
+ super(*actual_args, &actual_block)
157
+ end
158
+ }
126
159
  }
127
160
  end
128
161
 
129
- def __mock_block_call defi, actual_args, actual_block
130
- if block = defi.block
131
- arity = block.arity
132
- if arity < 0
133
- block.call(*actual_args , &actual_block)
134
- else
135
- block.call(*actual_args.first(arity), &actual_block)
136
- end
137
- end
138
- end
139
-
140
162
  def __mock_check_args expected_args, actual_args
141
163
  if expected_args == [WithAnyArgs]
142
164
  true
@@ -1,5 +1,5 @@
1
1
 
2
- require 'muack/definition'
2
+ require 'muack/error'
3
3
 
4
4
  module Muack
5
5
  class Modifier < Struct.new(:mock, :defi)
@@ -12,10 +12,21 @@ module Muack
12
12
  # Public API
13
13
  def returns val=nil, &block
14
14
  defi.block = block || lambda{ val }
15
+ self
16
+ end
17
+
18
+ # Public API
19
+ def proxy
20
+ defi.proxy = true
21
+ self
15
22
  end
16
23
 
17
24
  # Public API
18
25
  def times number
26
+ if mock.__mock_class == Stub
27
+ raise StubHasNoTimes.new(object, defi, number)
28
+ end
29
+
19
30
  if number >= 1
20
31
  (number - 1).times{ mock.__mock_defis_push(defi) }
21
32
  elsif number == 0
data/lib/muack/session.rb CHANGED
@@ -1,18 +1,17 @@
1
1
 
2
2
  require 'muack/mock'
3
3
  require 'muack/stub'
4
- require 'muack/proxy'
4
+ require 'muack/spy'
5
5
  require 'muack/any_instance_of'
6
6
 
7
7
  module Muack
8
8
  class Session < Hash
9
- def mock obj; self["mk #{obj.__id__}"] ||= Mock .new(obj); end
10
- def stub obj; self["sb #{obj.__id__}"] ||= Stub .new(obj); end
11
- def mock_proxy obj; self["mp #{obj.__id__}"] ||= MockProxy.new(obj); end
12
- def stub_proxy obj; self["sp #{obj.__id__}"] ||= StubProxy.new(obj); end
9
+ def mock obj; self["mk #{obj.__id__}"] ||= Mock.new(obj) ; end
10
+ def stub obj; self["sb #{obj.__id__}"] ||= Stub.new(obj) ; end
11
+ def spy obj; self["sy #{obj.__id__}"] ||= Spy .new(stub(obj)); end
13
12
 
14
- def any_instance_of klass
15
- (@any_instance_of ||= {})[klass.__id__] ||= AnyInstanceOf.new(klass)
13
+ def any_instance_of kls
14
+ (@others ||= {})["ai #{kls.__id__}"] ||= AnyInstanceOf.new(kls)
16
15
  end
17
16
 
18
17
  def verify
@@ -20,6 +19,7 @@ module Muack
20
19
  end
21
20
 
22
21
  def reset
22
+ instance_variable_defined?(:@others) && @others.clear
23
23
  each_value(&:__mock_reset)
24
24
  clear
25
25
  end
data/lib/muack/spy.rb ADDED
@@ -0,0 +1,23 @@
1
+
2
+ require 'muack/mock'
3
+
4
+ module Muack
5
+ class Spy < Mock
6
+ def initialize stub
7
+ super(stub.object)
8
+ @secret = stub.__mock_disps.values.flatten # steal disps
9
+ end
10
+
11
+ # used for Muack::Session#verify
12
+ def __mock_verify
13
+ @secret.each{ |defi| __mock_dispatch(defi.msg, defi.args) }
14
+ super # simulate dispatching before passing to mock to verify
15
+ end
16
+
17
+ # used for Muack::Session#reset, but spies never leave any track
18
+ def __mock_reset; end
19
+
20
+ private
21
+ def __mock_inject_method defi; end # spies don't leave any track
22
+ end
23
+ end
data/lib/muack/stub.rb CHANGED
@@ -7,12 +7,11 @@ module Muack
7
7
  def __mock_verify; true; end
8
8
 
9
9
  # used for mocked object to dispatch mocked method
10
- def __mock_dispatch msg, actual_args, actual_block
11
- defi = __mock_defis[msg].find{ |d|
12
- __mock_check_args(d.args, actual_args)
13
- }
14
- if defi
15
- __mock_block_call(defi, actual_args, actual_block)
10
+ def __mock_dispatch msg, actual_args
11
+ if defi = __mock_defis[msg].find{ |d|
12
+ __mock_check_args(d.args, actual_args) }
13
+ __mock_disps_push(defi) # our spies are interested in this
14
+ defi
16
15
  else
17
16
  Mock.__send__(:raise, # Wrong argument
18
17
  Unexpected.new(object, __mock_defis[msg], msg, actual_args))
data/lib/muack/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Muack
3
- VERSION = '0.5.2'
3
+ VERSION = '0.7.0'
4
4
  end
data/muack.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "muack"
5
- s.version = "0.5.2"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Lin Jen-Shin (godfat)"]
9
- s.date = "2013-06-26"
9
+ s.date = "2013-06-27"
10
10
  s.description = "Muack -- Yet another mocking library.\n\nBasically it's an [RR][] clone, but much faster under heavy use.\nIt's 32x times faster (750s vs 23s) for running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib"
11
11
  s.email = ["godfat (XD) godfat.org"]
12
12
  s.files = [
@@ -21,12 +21,13 @@ Gem::Specification.new do |s|
21
21
  "lib/muack.rb",
22
22
  "lib/muack/any_instance_of.rb",
23
23
  "lib/muack/definition.rb",
24
+ "lib/muack/error.rb",
24
25
  "lib/muack/failure.rb",
25
26
  "lib/muack/mock.rb",
26
27
  "lib/muack/modifier.rb",
27
- "lib/muack/proxy.rb",
28
28
  "lib/muack/satisfy.rb",
29
29
  "lib/muack/session.rb",
30
+ "lib/muack/spy.rb",
30
31
  "lib/muack/stub.rb",
31
32
  "lib/muack/test.rb",
32
33
  "lib/muack/version.rb",
@@ -2,8 +2,9 @@
2
2
  require 'muack/test'
3
3
 
4
4
  describe Muack::AnyInstanceOf do
5
+ klass = Class.new{ def f; 0; end }
6
+
5
7
  should 'mock any_instance_of' do
6
- klass = Class.new
7
8
  any_instance_of(klass){ |instance| mock(instance).say{ true } }
8
9
  obj = klass.new
9
10
  obj.say .should.eq true
@@ -13,17 +14,15 @@ describe Muack::AnyInstanceOf do
13
14
  end
14
15
 
15
16
  should 'proxy any_instance_of' do
16
- klass = Class.new{ def f; true; end }
17
- any_instance_of(klass){ |instance| mock_proxy(instance).f }
17
+ any_instance_of(klass){ |instance| mock(instance).f.proxy }
18
18
  obj = klass.new
19
- obj.f .should.eq true
19
+ obj.f .should.eq 0
20
20
  Muack.verify.should.eq true
21
- obj.f .should.eq true
21
+ obj.f .should.eq 0
22
22
  end
23
23
 
24
24
  should 'proxy any_instance_of with a block' do
25
- klass = Class.new{ def f; 0; end }
26
- any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+1 } }
25
+ any_instance_of(klass){ |instance| mock(instance).f{ |i| i+1 }.proxy }
27
26
  obj = klass.new
28
27
  obj.f .should.eq 1
29
28
  Muack.verify.should.eq true
@@ -31,9 +30,8 @@ describe Muack::AnyInstanceOf do
31
30
  end
32
31
 
33
32
  should 'proxy with multiple any_instance_of call' do
34
- klass = Class.new{ def f; 0; end }
35
- any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+1 } }
36
- any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+2 } }
33
+ any_instance_of(klass){ |instance| mock(instance).f{ |i| i+1 }.proxy }
34
+ any_instance_of(klass){ |instance| mock(instance).f{ |i| i+2 }.proxy }
37
35
  obj = klass.new
38
36
  obj.f.should.eq 1
39
37
  obj.f.should.eq 2
@@ -42,13 +40,54 @@ describe Muack::AnyInstanceOf do
42
40
  end
43
41
 
44
42
  should 'mock with multiple any_instance_of call' do
45
- klass = Class.new{ def f; 0; end }
46
43
  any_instance_of(klass){ |inst| mock(inst).f(is_a(Fixnum)){ |i| i+1 } }
47
44
  any_instance_of(klass){ |inst| mock(inst).f(is_a(Fixnum)){ |i| i+2 } }
48
45
  obj = klass.new
49
- obj.f(0).should.eq 1
50
- obj.f(0).should.eq 2
46
+ obj.f(2).should.eq 3
47
+ obj.f(2).should.eq 4
48
+ Muack.verify.should.eq true
49
+ obj.f.should.eq 0
50
+ end
51
+
52
+ should 'stub proxy with any_instance_of and spy' do
53
+ any_instance_of(klass){ |inst| stub(inst).f{ |i| i+3 }.proxy }
54
+ obj = klass.new
55
+ obj.f.should.eq 3
56
+ obj.f.should.eq 3
57
+ spy(any_instance_of(klass)).f.times(2)
51
58
  Muack.verify.should.eq true
52
59
  obj.f.should.eq 0
53
60
  end
61
+
62
+ should 'stub with any_instance_of and spy under satisfied' do
63
+ any_instance_of(klass){ |inst| stub(inst).f{ 5 } }
64
+ obj = klass.new
65
+ obj.f.should.eq 5
66
+ spy(any_instance_of(klass)).f.times(2)
67
+ begin
68
+ Muack.verify
69
+ rescue Muack::Expected => e
70
+ expected = /Muack::API\.any_instance_of\(.+?\)\.f\(\)/
71
+ e.expected .should =~ expected
72
+ e.expected_times.should.eq 2
73
+ e.actual_times .should.eq 1
74
+ end
75
+ obj.f.should.eq 0
76
+ end
77
+
78
+ should 'stub with any_instance_of and spy over satisfied' do
79
+ any_instance_of(klass){ |inst| stub(inst).f{ 2 } }
80
+ obj = klass.new
81
+ 2.times{ obj.f.should.eq 2 }
82
+ spy(any_instance_of(klass)).f
83
+ begin
84
+ Muack.verify
85
+ rescue Muack::Expected => e
86
+ expected = /Muack::API\.any_instance_of\(.+?\)\.f\(\)/
87
+ e.expected .should =~ expected
88
+ e.expected_times.should.eq 1
89
+ e.actual_times .should.eq 2
90
+ end
91
+ obj.f.should.eq 0
92
+ end
54
93
  end
data/test/test_mock.rb CHANGED
@@ -8,6 +8,10 @@ describe Muack::Mock do
8
8
  Muack::EnsureReset.call
9
9
  end
10
10
 
11
+ should 'inspect' do
12
+ mock(Obj).inspect.should.eq "Muack::API.mock(obj)"
13
+ end
14
+
11
15
  should 'mock with regular method' do
12
16
  mock(Obj).say(true){ 'boo' }
13
17
  Obj.say(true).should.eq 'boo'
@@ -58,10 +62,6 @@ describe Muack::Mock do
58
62
  mock.say{1}.object.say.should.eq 1
59
63
  end
60
64
 
61
- should 'inspect' do
62
- mock(Obj).inspect.should.eq "#<Muack::Mock object=obj>"
63
- end
64
-
65
65
  should 'mock and call, mock and call' do
66
66
  mock(Obj).say{0}
67
67
  Obj.say.should.eq 0
@@ -94,7 +94,7 @@ describe Muack::Mock do
94
94
  Muack::EnsureReset.call
95
95
  end
96
96
 
97
- should 'raise Muack::Unexpected error if passing unexpected argument' do
97
+ should 'raise Unexpected error if passing unexpected argument' do
98
98
  mock(Obj).say(true){ 'boo' }
99
99
  begin
100
100
  Obj.say(false)
@@ -144,7 +144,7 @@ describe Muack::Mock do
144
144
  end
145
145
  end
146
146
 
147
- should 'raise Muack::Unexpected when calling with diff sig' do
147
+ should 'raise Unexpected when calling with diff sig' do
148
148
  mock(Obj).say(true){1}
149
149
  Obj.say(true).should.eq 1
150
150
  begin
@@ -157,7 +157,7 @@ describe Muack::Mock do
157
157
  end
158
158
  end
159
159
 
160
- should 'raise Muack::Expected error if mock methods not called' do
160
+ should 'raise Expected error if mock methods not called' do
161
161
  mock(Obj).say(true){ 'boo' }
162
162
  begin
163
163
  Muack.verify
data/test/test_proxy.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  require 'muack/test'
3
3
 
4
- describe Muack::Proxy do
4
+ describe Muack::Mock do
5
5
  describe 'Muack.verify==true' do
6
6
  after do
7
7
  Muack.verify.should.eq true
@@ -9,48 +9,60 @@ describe Muack::Proxy do
9
9
  end
10
10
 
11
11
  should 'proxy with regular method' do
12
- mock_proxy(Str).reverse
12
+ mock(Str).reverse.proxy
13
13
  Str.reverse.should.eq 'ooM'
14
14
  end
15
15
 
16
16
  should 'proxy multiple times' do
17
- 2.times{ mock_proxy(Str).reverse }
17
+ 2.times{ mock(Str).reverse.proxy }
18
18
  2.times{ Str.reverse.should.eq 'ooM' }
19
19
  end
20
20
 
21
21
  should 'proxy multiple times with super method' do
22
- 2.times{ mock_proxy(Str).class }
22
+ 2.times{ mock(Str).class.proxy }
23
23
  2.times{ Str.class.should.eq String }
24
24
  end
25
25
 
26
+ should 'return modifier itself for any modifier methods' do
27
+ mock(Str).to_s.proxy.returns{ |s| s.reverse }.times(2).
28
+ with_any_args.with_any_args
29
+ 2.times{ Str.to_s.should.eq 'ooM' }
30
+ end
31
+
26
32
  should 'proxy and call the block' do
27
- mock_proxy(Obj).method_missing(:inspect){ |str| str.reverse }
33
+ mock(Obj).method_missing(:inspect){ |str| str.reverse }.proxy
28
34
  Obj.inspect.should.eq 'jbo'
29
35
  end
30
36
 
31
37
  should 'proxy and call the block with super' do
32
- mock_proxy(Str).class{ |k| k.name.reverse }
38
+ mock(Str).class{ |k| k.name.reverse }.proxy
33
39
  Str.class.should.eq 'gnirtS'
34
40
  end
35
41
 
36
- should 'mock_proxy and call, mock_proxy and call' do
37
- mock_proxy(Obj).class{ |k| k.name.reverse }
42
+ should 'mock proxy and call, mock proxy and call' do
43
+ mock(Obj).class{ |k| k.name.reverse }.proxy
38
44
  Obj.class.should.eq 'tcejbO'
39
- mock_proxy(Obj).class{ |k| k.name.upcase }
45
+ mock(Obj).class{ |k| k.name.upcase }.proxy
40
46
  Obj.class.should.eq 'OBJECT'
41
47
  end
42
48
 
43
- should 'stub_proxy and call, stub_proxy and call' do
44
- stub_proxy(Obj).kind_of?(Object){ |b| !b }
49
+ should 'stub proxy and call, stub proxy and call' do
50
+ stub(Obj).kind_of?(Object){ |b| !b }.proxy
45
51
  Obj.kind_of?(Object).should.eq false
46
- stub_proxy(Obj).kind_of?(String){ |b| b.to_s }
52
+ stub(Obj).kind_of?(String){ |b| b.to_s }.proxy
47
53
  Obj.kind_of?(String).should.eq 'false'
48
54
  end
49
55
 
50
- should 'stub_proxy with any times' do
51
- stub_proxy(Obj).class{ |k| k.name.downcase }
56
+ should 'stub proxy with any times' do
57
+ stub(Obj).class{ |k| k.name.downcase }.proxy
52
58
  3.times{ Obj.class.should.eq 'object' }
53
59
  end
60
+
61
+ should 'stub proxy and spy' do
62
+ stub(Obj).class{ |k| k.name.downcase }.proxy
63
+ Obj.class.should.eq 'object'
64
+ spy(Obj).class
65
+ end
54
66
  end
55
67
 
56
68
  describe 'Muack.verify==false' do
@@ -59,8 +71,8 @@ describe Muack::Proxy do
59
71
  Muack::EnsureReset.call
60
72
  end
61
73
 
62
- should 'raise Muack::Expected error if passing unexpected argument' do
63
- mock_proxy(Str).reverse
74
+ should 'raise Expected error if passing unexpected argument' do
75
+ mock(Str).reverse.proxy
64
76
  Str.reverse.should.eq 'ooM'
65
77
  begin
66
78
  Str.reverse
data/test/test_satisfy.rb CHANGED
@@ -17,7 +17,7 @@ describe Muack::Satisfy do
17
17
  Muack::EnsureReset.call
18
18
  end
19
19
 
20
- should 'raise Muack::Unexpected error if passing unexpected argument' do
20
+ should 'raise Unexpected error if passing unexpected argument' do
21
21
  mock(Obj).say(is_a(Array)){ 'boo' }
22
22
  begin
23
23
  Obj.say(false)
@@ -53,7 +53,7 @@ describe Muack::Satisfy do
53
53
  Muack::EnsureReset.call
54
54
  end
55
55
 
56
- should 'raise Muack::Unexpected error if passing unexpected argument' do
56
+ should 'raise Unexpected error if passing unexpected argument' do
57
57
  mock(Obj).say(anything){ 'boo' }
58
58
  begin
59
59
  Obj.say(6, 7)
@@ -84,7 +84,7 @@ describe Muack::Satisfy do
84
84
  Muack::EnsureReset.call
85
85
  end
86
86
 
87
- should 'raise Muack::Unexpected error if passing unexpected argument' do
87
+ should 'raise Unexpected error if passing unexpected argument' do
88
88
  mock(Obj).say(match(/\w/)){ 'boo' }
89
89
  begin
90
90
  Obj.say('!')
@@ -115,7 +115,7 @@ describe Muack::Satisfy do
115
115
  Muack::EnsureReset.call
116
116
  end
117
117
 
118
- should 'raise Muack::Unexpected error if passing unexpected argument' do
118
+ should 'raise Unexpected error if passing unexpected argument' do
119
119
  mock(Obj).say(hash_including(:b => 2)){ 'boo' }
120
120
  begin
121
121
  Obj.say(:a => 1)
@@ -151,7 +151,7 @@ describe Muack::Satisfy do
151
151
  Muack::EnsureReset.call
152
152
  end
153
153
 
154
- should 'raise Muack::Unexpected error if passing unexpected argument' do
154
+ should 'raise Unexpected error if passing unexpected argument' do
155
155
  mock(Obj).say(within(0..5)){ 'boo' }
156
156
  begin
157
157
  Obj.say(6)
@@ -182,7 +182,7 @@ describe Muack::Satisfy do
182
182
  Muack::EnsureReset.call
183
183
  end
184
184
 
185
- should 'raise Muack::Unexpected error if passing unexpected argument' do
185
+ should 'raise Unexpected error if passing unexpected argument' do
186
186
  mock(Obj).say(satisfy{ |arg| arg % 2 == 0 }){ 'boo' }
187
187
  begin
188
188
  Obj.say(1)
data/test/test_stub.rb CHANGED
@@ -2,12 +2,24 @@
2
2
  require 'muack/test'
3
3
 
4
4
  describe Muack::Stub do
5
+ should 'raise StubHasNoTimes with stub(obj).f.times(0)' do
6
+ lambda{ stub(Obj).f.times(0) }.should.raise(Muack::StubHasNoTimes)
7
+ end
8
+
5
9
  describe 'Muack.verify==true' do
6
10
  after do
7
11
  Muack.verify.should.eq true
8
12
  Muack::EnsureReset.call
9
13
  end
10
14
 
15
+ should 'inspect' do
16
+ stub(Obj).inspect.should.eq "Muack::API.stub(obj)"
17
+ end
18
+
19
+ should 'inspect' do
20
+ spy( Obj).inspect.should.eq "Muack::API.spy(obj)"
21
+ end
22
+
11
23
  should 'stub with regular method' do
12
24
  stub(Obj).say{ 'goo' }
13
25
  3.times{ Obj.say.should.eq 'goo' }
@@ -26,6 +38,24 @@ describe Muack::Stub do
26
38
  Obj.saya.should.eq 1
27
39
  Obj.say .should.eq 0
28
40
  end
41
+
42
+ should 'work with spy' do
43
+ stub(Obj).say{0}
44
+ Obj.say.should.eq 0
45
+ spy(Obj).say
46
+ end
47
+
48
+ should 'work with spy twice' do
49
+ stub(Obj).say
50
+ 2.times{ Obj.say.should.eq nil }
51
+ spy(Obj).say.times(2)
52
+ end
53
+
54
+ should 'work with spy spy' do
55
+ stub(Obj).say
56
+ 2.times{ Obj.say.should.eq nil }
57
+ 2.times{ spy(Obj).say }
58
+ end
29
59
  end
30
60
 
31
61
  describe 'Muack.verify==false' do
@@ -34,7 +64,7 @@ describe Muack::Stub do
34
64
  Muack::EnsureReset.call
35
65
  end
36
66
 
37
- should 'raise Muack::Unexpected error if passing unexpected argument' do
67
+ should 'raise Unexpected error if passing unexpected argument' do
38
68
  stub(Obj).say(true){ 'boo' }
39
69
  begin
40
70
  Obj.say(false)
@@ -58,5 +88,66 @@ describe Muack::Stub do
58
88
  e.message .should.eq "\nExpected: #{e.expected}\n but was: #{e.was}"
59
89
  end
60
90
  end
91
+
92
+ should 'raise Expected if the spy is not satisfied' do
93
+ stub(Obj).say
94
+ spy( Obj).say
95
+ begin
96
+ Muack.verify
97
+ 'never'.should.eq 'reach'
98
+ rescue Muack::Expected => e
99
+ e.expected .should.eq 'obj.say()'
100
+ e.expected_times.should.eq 1
101
+ e.actual_times .should.eq 0
102
+ e.message .should.eq "\nExpected: obj.say()\n " \
103
+ "called 1 times\n but was 0 times."
104
+ end
105
+ end
106
+
107
+ should 'raise Expected if the spy is not satisfied enough' do
108
+ stub(Obj).say
109
+ Obj.say
110
+ spy( Obj).say(0)
111
+ begin
112
+ Muack.verify
113
+ 'never'.should.eq 'reach'
114
+ rescue Muack::Unexpected => e
115
+ e.expected.should.eq "obj.say(0)"
116
+ e.was .should.eq 'obj.say()'
117
+ e.message .should.eq "\nExpected: #{e.expected}\n but was: #{e.was}"
118
+ end
119
+ end
120
+
121
+ should 'show correct times for under satisfaction' do
122
+ stub(Obj).say
123
+ 2.times{ Obj.say }
124
+ spy( Obj).say.times(3)
125
+ begin
126
+ Muack.verify
127
+ 'never'.should.eq 'reach'
128
+ rescue Muack::Expected => e
129
+ e.expected .should.eq 'obj.say()'
130
+ e.expected_times.should.eq 3
131
+ e.actual_times .should.eq 2
132
+ e.message .should.eq "\nExpected: obj.say()\n " \
133
+ "called 3 times\n but was 2 times."
134
+ end
135
+ end
136
+
137
+ should 'show correct times for over satisfaction' do
138
+ stub(Obj).say
139
+ 2.times{ Obj.say }
140
+ spy( Obj).say
141
+ begin
142
+ Muack.verify
143
+ 'never'.should.eq 'reach'
144
+ rescue Muack::Expected => e
145
+ e.expected .should.eq 'obj.say()'
146
+ e.expected_times.should.eq 1
147
+ e.actual_times .should.eq 2
148
+ e.message .should.eq "\nExpected: obj.say()\n " \
149
+ "called 1 times\n but was 2 times."
150
+ end
151
+ end
61
152
  end
62
153
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: muack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lin Jen-Shin (godfat)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-26 00:00:00.000000000 Z
11
+ date: 2013-06-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  Muack -- Yet another mocking library.
@@ -35,12 +35,13 @@ files:
35
35
  - lib/muack.rb
36
36
  - lib/muack/any_instance_of.rb
37
37
  - lib/muack/definition.rb
38
+ - lib/muack/error.rb
38
39
  - lib/muack/failure.rb
39
40
  - lib/muack/mock.rb
40
41
  - lib/muack/modifier.rb
41
- - lib/muack/proxy.rb
42
42
  - lib/muack/satisfy.rb
43
43
  - lib/muack/session.rb
44
+ - lib/muack/spy.rb
44
45
  - lib/muack/stub.rb
45
46
  - lib/muack/test.rb
46
47
  - lib/muack/version.rb
data/lib/muack/proxy.rb DELETED
@@ -1,39 +0,0 @@
1
-
2
- require 'muack/mock'
3
- require 'muack/stub'
4
-
5
- module Muack
6
- module Proxy
7
- def __mock_block_call defi, actual_args, actual_block
8
- # handle block call in injected method, since we need to call origin
9
- defi # but we still want to know which defi gets dispatched!
10
- end
11
-
12
- def __mock_inject_mock_method target, defi
13
- mock = self # remember the context
14
- target.__send__(:define_method, defi.msg){|*actual_args, &actual_block|
15
- d = mock.__mock_dispatch(defi.msg, actual_args, actual_block)
16
-
17
- ret = if d.original_method
18
- __send__(d.original_method, *actual_args, &actual_block)
19
- else
20
- super(*actual_args, &actual_block)
21
- end
22
-
23
- if d.block
24
- d.block.call(ret)
25
- else
26
- ret
27
- end
28
- }
29
- end
30
- end
31
-
32
- class MockProxy < Mock
33
- include Proxy
34
- end
35
-
36
- class StubProxy < Stub
37
- include Proxy
38
- end
39
- end