muack 0.7.3 → 1.0.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: 0f70136437354cf553340e7a4502e6fc4056802e
4
- data.tar.gz: 4f73fe1b681f0e6b4b837f89e72865a008f721af
3
+ metadata.gz: fbb0455780531e5a3b3dc2519ba3fa1179f65866
4
+ data.tar.gz: 91445ca9d049d32bfb110566e4403883158071e5
5
5
  SHA512:
6
- metadata.gz: 832585b3ec72cf71fdd5fd1a607034ddd999e509d03e8239037fc2bb7a3a9d1637996a09ec2e1d2d45ace2539d3b8d4ee5c6877df02ed3a0113e3e2f125d8869
7
- data.tar.gz: c9c64a40ec5c10d2cbdf30d17c3952b857915160f7599f8a2258db88ad53d668d75c5f493399d0f315c811d9164ab41267498b329fa2bf8202f2f0dcfe93893f
6
+ metadata.gz: 9e02fc19cb16d91758b6122e2e704ea81ff298e7766806e1bf08a29be6daebe113635ca943ef0308ccea9cb345d60e46f1699112869b9f3c5a4883cc9c5d5cc3
7
+ data.tar.gz: 5a4e374c7f7cd81f379817143b7b82939f76a918b6aac6dfc44fd452823fc6a570111a56c033f416630b93106a9b02d81808ba91e342d2a6e27f1107b1d3c66e
data/.gitignore CHANGED
@@ -1,2 +1 @@
1
1
  /pkg/
2
- *.rbc
data/.travis.yml CHANGED
@@ -1,11 +1,9 @@
1
1
  before_install: 'git submodule update --init'
2
2
  script: 'ruby -r bundler/setup -S rake test'
3
3
 
4
- env:
5
- - 'RBXOPT=-X19'
6
-
7
4
  rvm:
8
5
  - 1.9.3
9
6
  - 2.0.0
10
- - rbx-head
11
- - jruby-head
7
+ - 2.1.0
8
+ - rbx
9
+ - jruby
data/CHANGES.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # CHANGES
2
2
 
3
+ ## Muack 1.0.0 -- ?
4
+
5
+ Improvements:
6
+
7
+ * The internal conflicting method names are now a bit more informative
8
+ and unique thus less likely to have conflicts.
9
+
10
+ * Fixed a bug where mock and stub with the same method were defined.
11
+ Previously, it would raise an undefined method error upon verifying
12
+ while we're removing injected method. Now it could properly undefine
13
+ injected methods.
14
+
15
+ * Fixed issues mocking private methods. Now it would not only work without
16
+ a problem, but also preserve the privilege if the original method is a
17
+ private method. Note that for now, protected methods are treated as
18
+ public methods though.
19
+
20
+ * Fixed a bug where user customized Satisfy could crash if it's located
21
+ on a top-level. i.e. class names without ::.
22
+
23
+ Incompatible changes:
24
+
25
+ * Removed proxy method. From now on, if you do not pass a block to a
26
+ mock, then it would assume it's a proxy. You can think of instead of
27
+ make an empty block as a default, the original method is the default.
28
+ That means, previously, mock without a block would always return nil,
29
+ but now it instead means a proxy.
30
+
31
+ * Introduced peek_args method. Sometimes I really need to peek the
32
+ arguments of a method, or trying to provide a different argument
33
+ based on the original argument. So peek_args is the way to do it.
34
+
35
+ * Introduced peek_return method. By duality, we also introduce something
36
+ we can peek the return value. Using this along with a custom block
37
+ doesn't really make sense, but this is actually the previous proxy
38
+ block. Previously, if a mock is a proxy, then the block doesn't really
39
+ mean the implementation, but a modification to the original return.
40
+ That is, the current peek_return. Originally the block is quite
41
+ inconsistent as the semantics of the block would change depending on
42
+ it is a proxy or not.
43
+
44
+ So to proxy the to_s method and then reverse the result, you write:
45
+
46
+ ``` ruby
47
+ str = 'str'
48
+ Muack::API.mock(str).to_s.peek_return{ |s| s.reverse }
49
+ p str.to_s # => 'rts'
50
+ ```
51
+
52
+ * Removed plain value argument in `returns`. From now on, we should
53
+ always use the block form. Instead, the argument was changed to be
54
+ an optional option for specifying if the underlying block should be
55
+ instance executed or not. By default, the block is lexical scoped.
56
+ If passing `:instance_exec => true` to `returns`, `peek_args`, and
57
+ `peek_return`, then the block is instead instance scoped, passing
58
+ to the instance's `instance_exec`. This way, we would be able to
59
+ touch the inside of mocked object.
60
+
61
+ Without passing `:instance_exec => true`, `to_i` would be called on
62
+ the top-level object instead. By passing this argument, `to_i` would be
63
+ called in the string.
64
+
65
+ ``` ruby
66
+ str = '123'
67
+ Muack::API.mock(str).int.returns(:instance_exec => true){to_i}
68
+ p str.int # => 123
69
+ ```
70
+
3
71
  ## Muack 0.7.3 -- 2013-10-01
4
72
 
5
73
  * Added `Muack::API.including(element)` for detecting if the underlying
data/Gemfile CHANGED
@@ -5,3 +5,7 @@ gemspec
5
5
 
6
6
  gem 'rake'
7
7
  gem 'bacon'
8
+
9
+ platform :rbx do
10
+ gem 'rubysl-singleton' # used in rake
11
+ end
data/README.md CHANGED
@@ -10,10 +10,10 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
10
10
 
11
11
  ## DESCRIPTION:
12
12
 
13
- Muack -- Yet another mocking library.
13
+ Muack -- A fast, small, yet powerful mocking library.
14
14
 
15
- Basically it's an [RR][] clone, but much faster under heavy use.
16
- It's 32x times faster (750s vs 23s) for running [Rib][] tests.
15
+ Inspired by [RR][], and it's 32x times faster (750s vs 23s) than RR
16
+ for running [Rib][] tests.
17
17
 
18
18
  [RR]: https://github.com/rr/rr
19
19
  [Rib]: https://github.com/godfat/rib
@@ -21,11 +21,11 @@ It's 32x times faster (750s vs 23s) for running [Rib][] tests.
21
21
  ## WHY?
22
22
 
23
23
  Because RR has/had some bugs and it is too complex for me to fix it.
24
- Muack is much simpler and thus much faster and less likely to have bugs.
24
+ Muack is much simpler and thus much faster and much more consistent.
25
25
 
26
26
  ## REQUIREMENTS:
27
27
 
28
- * Tested with MRI (official CRuby) 1.9.3, 2.0.0, Rubinius and JRuby.
28
+ * Tested with MRI (official CRuby) 2.0.0, 2.1.0, Rubinius and JRuby.
29
29
 
30
30
  ## INSTALLATION:
31
31
 
@@ -33,7 +33,7 @@ Muack is much simpler and thus much faster and less likely to have bugs.
33
33
 
34
34
  ## SYNOPSIS:
35
35
 
36
- Basically it's an [RR][] clone. Let's see a [Bacon][] example.
36
+ Here's a quick example using [Bacon][].
37
37
 
38
38
  ``` ruby
39
39
  require 'bacon'
@@ -55,497 +55,944 @@ end
55
55
 
56
56
  [Bacon]: https://github.com/chneukirchen/bacon
57
57
 
58
- ### Coming from RR?
58
+ ### Overview
59
59
 
60
- Basically since it's an RR clone, the APIs are much the same.
61
- Let's see what's the different with code snippets. All codes
62
- were extracted from
63
- [RR's API document](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md).
60
+ There are 3 parts in Muack, which are:
64
61
 
65
- #### [mock](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mock)
62
+ * Mocks
63
+ * Mocks Modifiers
64
+ * Arguments Verifiers (Satisfy)
66
65
 
67
- `mock` is the same as RR.
66
+ Mocks are objects with injected methods which we could observe, and mocks
67
+ modifiers are telling how we want to observe the mocks, and finally argument
68
+ verifiers could help us observe the arguments passed to the injected methods.
69
+
70
+ Let's explain them one by one.
71
+
72
+ ### Mocks
73
+
74
+ There are also 3 different kinds of mocks in Muack, which are:
75
+
76
+ * Mocks
77
+ * Stubs
78
+ * Spies
79
+
80
+ You could also think of _mocks_ are _stubs_ + _spies_. Here's the equation:
81
+
82
+ mock = stub + spy
83
+
84
+ Stubs help us inject methods into the objects we want to observe. Spies
85
+ help us observe the behaviours of the objects. As for mocks, they inject
86
+ methods and observe the behaviours in realtime. They complain immediately
87
+ if the behaviours were unexpected. In contrast, if we're not asking spies,
88
+ stubs won't complain themselves.
89
+
90
+ Here's an example using a mock:
68
91
 
69
92
  ``` ruby
70
- view = controller.template
71
- mock(view).render(:partial => "user_info") {"Information"}
93
+ obj = Object.new
94
+ mock(obj).name{ 'obj' }
95
+ p obj.name # 'obj'
96
+ p Muack.verify # true
72
97
  ```
73
98
 
74
- There's no `twice` modifier in Muack, use `times(2)` instead.
99
+ Which is roughly semantically equivalent to using a stub with a spy:
75
100
 
76
101
  ``` ruby
77
- mock(view).render.with_any_args.times(2) do |*args|
78
- if args.first == {:partial => "user_info"}
79
- "User Info"
80
- else
81
- "Stuff in the view #{args.inspect}"
82
- end
83
- end
102
+ obj = Object.new
103
+ stub(obj).name{ 'obj' }
104
+ p obj.name # 'obj'
105
+ spy(obj).name
106
+ p Muack.verify # true
84
107
  ```
85
108
 
86
- #### [stub](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stub)
109
+ You might wonder, then why mocks or why stubs with spies? The advantage of
110
+ using mocks is that, you only need to specify once. I guess this is quite
111
+ obvious. However, sometimes we don't care if the injected methods are called
112
+ or not, but sometimes we do care. With stubs and spies, we could always put
113
+ stubs in the before/setup block, and only when we really care if they are
114
+ called or not, we put spies to examine.
87
115
 
88
- `stub` is the same as RR.
116
+ On the other hand, stubs aren't limited to testing. If we want to monkey
117
+ patching something, stubs could be useful as we don't care how many times
118
+ the injected methods are called. Jump to _Muack as a mocky patching library_
119
+ section for more detail.
120
+
121
+ Note that you could also mix mocks and stubs for a given object.
122
+ Here's an example:
89
123
 
90
124
  ``` ruby
91
- jane = User.new
92
- bob = User.new
93
- stub(User).find('42') {jane}
94
- stub(User).find('99') {bob}
95
- stub(User).find do |id|
96
- raise "Unexpected id #{id.inspect} passed to me"
97
- end
125
+ obj = Object.new
126
+ stub(obj).name{ 'obj' }
127
+ mock(obj).id { 12345 }
128
+ p obj.name # 'obj'
129
+ p obj.id # 12345
130
+ p Muack.verify # true
98
131
  ```
99
132
 
100
- #### [times(0)](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#dont_allow-aliased-to-do_not_allow-dont_call-and-do_not_call)
133
+ However you should not mix mocks and stubs with the same method, or you
134
+ might encounter some unexpected result. Jump to _Caveat_ for more detail.
101
135
 
102
- There's no dont_allow method in Muack, use `times(0)` instead.
136
+ #### Anonymous mode
137
+
138
+ Sometimes we just want to stub something without a concrete object in mind.
139
+ By calling `mock` or `stub` without any argument, we're creating an anonymous
140
+ mock/stub. This is because the default argument for `mock` and `stub` is just
141
+ `Object.new`.
142
+
143
+ But how do we access the anonymously created object? We'll use the `object`
144
+ method on the modifier to access it. Here's an example:
103
145
 
104
146
  ``` ruby
105
- User.find('42').times(0)
106
- User.find('42') # raises a Muack::Unexpected
147
+ obj = mock.name{ 'obj' }.object
148
+ p obj.name # 'obj'
149
+ p Muack.verify # true
107
150
  ```
108
151
 
109
- #### [proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
110
-
111
- Instead of calling `proxy` immediately after calling `mock`, we put
112
- `proxy` the last because it's a method from `Muack::Modifier`.
152
+ This is exactly equivalent to this:
113
153
 
114
154
  ``` ruby
115
- view = controller.template
116
- mock(view).render(:partial => "right_navigation").proxy
117
- mock(view).render(:partial => "user_info") do |html|
118
- html.should include("John Doe")
119
- "Different html"
120
- end.proxy
155
+ mock(obj = Object.new).name{ 'obj' }
156
+ p obj.name # 'obj'
157
+ p Muack.verify # true
121
158
  ```
122
159
 
123
- If you feel it is weird to put proxy the last, you can also use
124
- `returns` modifier to put the block last as this:
160
+ Also, if we want to mock over multiple methods, we could also take the
161
+ advantage of block form of `mock` and `stub` method.
125
162
 
126
163
  ``` ruby
127
- view = controller.template
128
- mock(view).render(:partial => "right_navigation").proxy
129
- mock(view).render(:partial => "user_info").proxy.returns do |html|
130
- html.should include("John Doe")
131
- "Different html"
132
- end
164
+ obj = mock{ |m|
165
+ m.name{ 'obj' }
166
+ m.id { 12345 }
167
+ }.object
168
+ p obj.name # 'obj'
169
+ p obj.id # 12345
170
+ p Muack.verify # true
133
171
  ```
134
172
 
135
- The same goes to `stub`.
173
+ We can't omit the `object` method here because after defining the injected
174
+ method, we'll get a modifier to describe the properties of the injected
175
+ method. Jump to _Mocks Modifiers_ for details.
176
+
177
+ #### Proxy mode
178
+
179
+ There are chances that we don't really want to change the underlying
180
+ implementation for a given method, but we still want to make sure the
181
+ named method is called, and that's what we're testing for.
182
+
183
+ In those cases, proxy mode would be quite helpful. To turn a mock or stub
184
+ into proxy mode we simply do not provide any block to the injected method,
185
+ but just name it. Here's an example:
136
186
 
137
187
  ``` ruby
138
- view = controller.template
139
- stub(view).render(:partial => "user_info") do |html|
140
- html.should include("Joe Smith")
141
- html
142
- end.proxy
188
+ str = 'str'
189
+ mock(str).reverse
190
+ p str.reverse # 'rts'
191
+ p Muack.verify # true
143
192
  ```
144
193
 
145
- Or use `returns`:
194
+ Note that if reverse was not called exactly once, the mock would complain.
195
+ We could also use stub + spy to do the same thing as well:
146
196
 
147
197
  ``` ruby
148
- view = controller.template
149
- stub(view).render(:partial => "user_info").proxy.returns do |html|
150
- html.should include("Joe Smith")
151
- html
152
- end
198
+ str = 'str'
199
+ stub(str).reverse
200
+ p str.reverse # 'rts'
201
+ spy(str).reverse
202
+ p Muack.verify # true
153
203
  ```
154
204
 
155
- #### [any_instance_of](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#any_instance_of)
205
+ You might also want to use `peek_args` and `peek_return` modifier along with
206
+ proxies in order to slightly tweak the original implementation. Jump to
207
+ _Muack as a mocky patching library_ section for more detail.
208
+
209
+ #### any_instance_of mode
210
+
211
+ We only talked about mocking a specific object, but never mentioned what if
212
+ the objects we want to mock aren't at hand at the time we define mocks?
213
+ In those cases, instead of trying to mock object creation and return the
214
+ mock we defined, we might want to simply mock any instance of a particular
215
+ class, since this would make the process much easier.
156
216
 
157
- Only this form of `any_instance_of` is supported. On the other hand,
158
- any of the above is supported as well, not only stub.
217
+ Here we could use a special "mock" called `any_instance_of`, which takes a
218
+ class and returns a `Muack::AnyInstanceOf` which represents the instance of
219
+ the class we just passed. Having this special representation, we could treat
220
+ it as if a real instance and define regular mocks/stubs on it. It would then
221
+ applies to any instance of the class we gave.
222
+
223
+ Example speaks:
159
224
 
160
225
  ``` ruby
161
- any_instance_of(User) do |u|
162
- stub(u).valid? { false }
163
- mock(u).errors { [] }
164
- mock(u).save.proxy
165
- stub(u).reload.proxy
166
- end
226
+ array = any_instance_of(Array)
227
+ stub(array).name{ 'array' }
228
+ p [ ].name # 'array'
229
+ p [0].name # 'array'
230
+ p Muack.verify # true
167
231
  ```
168
232
 
169
- #### [Spies](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#spies)
233
+ And as most of the time we don't care about the representation after mocks
234
+ were defined, we could use the block form:
170
235
 
171
- We don't try to provide different methods for different testing framework,
172
- so that we don't have to create so many testing framework adapters, and
173
- try to be smart to find the correct adapter. There are simply too many
174
- testing frameworks out there. Ruby's built-in test/unit and minitest have
175
- a lot of different versions, so does rspec.
236
+ ``` ruby
237
+ any_instance_of(Array) do |array|
238
+ stub(array).name{ 'array' }
239
+ stub(array).id { 1234567 }
240
+ end
241
+ p [ ].name # 'array'
242
+ p [0].id # 1234567
243
+ p Muack.verify # true
244
+ ```
176
245
 
177
- Here we just try to do it the Muack's way:
246
+ Note that if you need to access the real instance instead of the
247
+ representation in the injected method, you might want to enable
248
+ instance_exec mode. Please jump to _instance_exec mode_ section
249
+ for more detail.
178
250
 
251
+ Here's an quick example:
179
252
 
180
253
  ``` ruby
181
- subject = Object.new
182
- stub(subject).foo(1)
183
- subject.foo(1)
184
-
185
- spy(subject).foo(1)
186
- spy(subject).bar # This doesn't verify immediately.
187
- Muack.verify # This fails, saying `bar` was never called.
254
+ any_instance_of(Array) do |array|
255
+ p array.class # Muack::AnyInstanceOf
256
+ mock(array).name.returns(:instance_exec => true){ inspect }
257
+ end
258
+ p [0, 1].name # '[0, 1]'
259
+ p Muack.verify # true
188
260
  ```
189
261
 
190
- #### [Block form](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#block-syntax)
191
-
192
- Block form is also supported. However we don't support `instance_eval` form.
193
- There's little point to use instance_eval since it's much more complicated
194
- and much slower.
262
+ Lastly, you could also use `any_instance_of` along with proxy mode,
263
+ or any other combination you could think of:
195
264
 
196
265
  ``` ruby
197
- script = MyScript.new
198
- mock(script) do |expect|
199
- expect.system("cd #{RAILS_ENV}") {true}
200
- expect.system("rake foo:bar") {true}
201
- expect.system("rake baz") {true}
266
+ any_instance_of(Array) do |array|
267
+ stub(array).name{ 'array' }
268
+ mock(array).max
202
269
  end
270
+ p [ ].name # 'array'
271
+ p [0].max # 0
272
+ p Muack.verify # true
203
273
  ```
204
274
 
205
- #### [Nested mocks](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#double-graphs)
275
+ Though you should still not mix mocks and stubs with the same method,
276
+ and as you could tell from the above example, Muack would not complain
277
+ for every array without calling `max` once. This is because any_instance_of
278
+ would count on all instances, instead of individual instances. Here
279
+ we're actually telling Muack that `max` should be called exactly once
280
+ amongst all instances of array, and it is indeed called exactly once
281
+ amongst two instances here.
282
+
283
+ This might or might not be what we want. But think it twice, if we're
284
+ mocking any instance of a very basic class in Ruby, testing against
285
+ individual instances could be too strict since it's used everywhere!
286
+
287
+ Please check _Caveat_ section for more details.
288
+
289
+ ### Mocks Modifiers
290
+
291
+ A modifier is something specifying a property of an injected method.
292
+ By making a mock/stub/spy, it would return a modifier descriptor which
293
+ we could then specify properties about the injected method.
294
+
295
+ Note that we could chain properties for a given modifier descriptor
296
+ because all public methods for declaring a property would return the
297
+ modifier descriptor itself. Let's see the specific usages for each
298
+ properties with concrete examples.
299
+
300
+ #### times
206
301
 
207
- The shortest API (which might be a bit tricky) is not supported,
208
- but we do support:
302
+ By using mocks, we are saying that the injected method should be called
303
+ exactly once. However the injected method might be called more than once,
304
+ say, twice. We could specify this with `times` modifier:
209
305
 
210
306
  ``` ruby
211
- stub(object).foo { stub.bar{ :baz }.object }
212
- object.foo.bar #=> :baz
307
+ obj = Object.new
308
+ mock(obj).name{ 'obj' }.times(2)
309
+ p obj.name # 'obj'
310
+ p obj.name # 'obj'
311
+ p Muack.verify # true
213
312
  ```
214
313
 
215
- And of course the verbose way:
314
+ This is actually also semantically equivalent to making the mock twice:
216
315
 
217
316
  ``` ruby
218
- bar = stub.bar{ :baz }.object
219
- stub(object).foo { bar }
220
- object.foo.bar #=> :baz
317
+ obj = Object.new
318
+ mock(obj).name{ 'obj' }
319
+ mock(obj).name{ 'obj' }
320
+ p obj.name # 'obj'
321
+ p obj.name # 'obj'
322
+ p Muack.verify # true
221
323
  ```
222
324
 
223
- Or even more verbose, of course:
325
+ Note that it does not make sense to specify `times` for stubs, because
326
+ stubs don't care about times. Spies do, though. So this is also
327
+ semantically equivalent to below:
224
328
 
225
329
  ``` ruby
226
- bar = Object.new
227
- stub(bar).bar{ :baz }
228
- stub(object).foo { bar }
229
- object.foo.bar #=> :baz
330
+ obj = Object.new
331
+ stub(obj).name{ 'obj' }
332
+ p obj.name # 'obj'
333
+ p obj.name # 'obj'
334
+ spy(obj).name.times(2)
335
+ p Muack.verify # true
230
336
  ```
231
337
 
232
- #### [Modifier](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#modifying-doubles)
233
-
234
- After defining a mock method, you get a `Muack::Modifier` back.
338
+ Or without using times for spy:
235
339
 
236
340
  ``` ruby
237
- stub(object).foo #=> Muack::Modifier
341
+ obj = Object.new
342
+ stub(obj).name{ 'obj' }
343
+ p obj.name # 'obj'
344
+ p obj.name # 'obj'
345
+ spy(obj).name
346
+ spy(obj).name
347
+ p Muack.verify # true
238
348
  ```
239
349
 
240
- However, you cannot flip around methods like RR. Whenever you define a
241
- mock/stub method, you must provide the block immediately.
350
+ The advantage of specifying mocks twice is that we could actually provide
351
+ different results for each call. You could think of it as a stack. Here's
352
+ a simple example:
242
353
 
243
354
  ``` ruby
244
- mock(object).foo{ 'bar' }.times(2)
355
+ obj = Object.new
356
+ mock(obj).name{ 0 }
357
+ mock(obj).name{ 1 }
358
+ mock(obj).name{ 2 }
359
+ p obj.name # 0
360
+ p obj.name # 1
361
+ p obj.name # 2
362
+ p Muack.verify # true
245
363
  ```
246
364
 
247
- If unfortunately, the method name you want to mock is already defined,
248
- you can call `method_missing` directly to mock it. For example, `inspect`
249
- is already defined in `Muack::Mock` to avoid crashing with [Bacon][].
250
- In this case, you should do this to mock `inspect`:
365
+ We could also use the block form for convenience:
251
366
 
252
367
  ``` ruby
253
- mock(object).method_missing(:inspect){ 'bar' }.times(2)
368
+ obj = Object.new
369
+ mock(obj) do |m|
370
+ m.name{ 0 }
371
+ m.name{ 1 }
372
+ m.name{ 2 }
373
+ end
374
+ p obj.name # 0
375
+ p obj.name # 1
376
+ p obj.name # 2
377
+ p Muack.verify # true
254
378
  ```
255
379
 
256
- #### [Stubbing method implementation / return value](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stubbing-method-implementation--return-value)
257
-
258
- Again, we embrace one true API to avoid confusion, unless the alternative
259
- API really has a great advantage. So we encourage people to use the block to
260
- return values. However, sometimes you cannot easily do that for certain
261
- methods due to Ruby's syntax. For example, you can't pass a block to
262
- a subscript operator `[]`. As a workaround, you can do it with
263
- `method_missing`, though it's not very obvious if you don't know
264
- what is `method_missing`.
380
+ Note that this does not apply to stubs because stubs never run out, thus
381
+ making stubs defined later have no effects at all.
265
382
 
266
383
  ``` ruby
267
- stub(object).method_missing(:[], is_a(Fixnum)){ |a| a+1 }
268
- object[1] #=> 2
384
+ obj = Object.new
385
+ stub(obj) do |m|
386
+ m.name{ 0 }
387
+ m.name{ 1 }
388
+ m.name{ 2 }
389
+ end
390
+ p obj.name # 0
391
+ p obj.name # 0
392
+ p obj.name # 0
393
+ p Muack.verify # true
269
394
  ```
270
395
 
271
- Instead you can do this with `returns`:
396
+ Note that if you do not want a given method be called at all, you could
397
+ use `times(0)` to enforce this.
398
+
399
+ #### with_any_args
400
+
401
+ We haven't talked about verifying arguments. With `with_any_args` modifier,
402
+ we're saying that we don't care about the arguments. If we're not specifying
403
+ any arguments like above examples, we're saying there's no arguments at all.
404
+
405
+ Here we'll show an example for `with_any_args`. If you do want to verify some
406
+ specific arguments, jump to _Arguments Verifiers_ section.
272
407
 
273
408
  ``` ruby
274
- stub(object)[is_a(Fixnum)].returns{ |a| a + 1 }
275
- object[1] #=> 2
409
+ obj = Object.new
410
+ mock(obj).name{ 'obj' }.with_any_args.times(4)
411
+ p obj.name # 'obj'
412
+ p obj.name(1) # 'obj'
413
+ p obj.name(nil) # 'obj'
414
+ p obj.name(true) # 'obj'
415
+ p Muack.verify # true
276
416
  ```
277
417
 
278
- You can also pass a value directly to `returns` if you only want to return
279
- a simple value.
418
+ #### returns
419
+
420
+ For some methods, we can't really pass a block to specify the implementation.
421
+ For example, we can't pass a block to `[]`, which is a Ruby syntax limitation.
422
+ To workaround it, we could use `returns` property:
280
423
 
281
424
  ``` ruby
282
- stub(object)[is_a(Fixnum)].returns(2)
283
- object[1] #=> 2
425
+ obj = Object.new
426
+ mock(obj)[0].returns{ 0 }
427
+ p obj[0] # 0
428
+ p Muack.verify # true
284
429
  ```
285
430
 
286
- On the other hand, since Muack is more strict than RR. Passing no arguments
287
- means you really don't want any argument. Here we need to specify the
288
- argument for Muack. The example in RR should be changed to this in Muack:
431
+ This is also useful when we want to put the implementation block in the last
432
+ instead of the beginning. Here's an example:
289
433
 
290
434
  ``` ruby
291
- stub(object).foo(is_a(Fixnum), anything){ |age, count, &block|
292
- raise 'hell' if age < 16
293
- ret = block.call count
294
- blue? ? ret : 'whatever'
295
- }
435
+ obj = Object.new
436
+ mock(obj).name.times(2).with_any_args.returns{ 'obj' }
437
+ p obj.name # 'obj'
438
+ p obj.name # 'obj'
439
+ p Muack.verify # true
296
440
  ```
297
441
 
298
- #### [Stubbing method implementation based on argument expectation](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stubbing-method-implementation-based-on-argument-expectation)
442
+ On the other hand, there's also another advantage of using `returns` than
443
+ passing the block directly to the injected method. With `returns`, there's
444
+ an additional option we could use by passing arguments to `returns`. We
445
+ can't do this in regular injected method definition because those arguments
446
+ are for verifying the actual arguments. Jump to _Arguments Verifiers_ section
447
+ for details.
299
448
 
300
- Here is exactly the same as RR.
449
+ The only option right now is `:instance_exec`.
301
450
 
302
- ``` ruby
303
- stub(object).foo { 'bar' }
304
- stub(object).foo(1, 2) { 'baz' }
305
- object.foo #=> 'bar'
306
- object.foo(1, 2) #=> 'baz'
307
- ```
451
+ #### instance_exec mode
452
+
453
+ By default, the block passed to the injected method is lexically/statically
454
+ scoped. That means, the scope is bound to the current binding. This is the
455
+ default because usually we don't need dynamic scopes, and we simply want to
456
+ return a plain value, and this is much easier to understand, and it is the
457
+ default for most programming languages, and it would definitely reduce
458
+ surprises. If we really need to operate on the object, we have it, and
459
+ we could touch the internal by calling instance_eval on the object.
308
460
 
309
- #### [Stubbing method to yield given block](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stubbing-method-to-yield-given-block)
461
+ However, things are a bit different if we're using `any_instance_of`.
462
+ If we're using `any_instance_of`, then we don't have the instance at
463
+ hand at the time we're defining the block, but only a `Muack::AnyInstanceOf`
464
+ instance to represent the instance. There's no way we could really touch
465
+ the object without `instance_exec` option.
310
466
 
311
- Always use the block to pass whatever back.
467
+ This would also be extremely helpful if we're using Muack as a monkey
468
+ patching library. We don't have to copy the original codes in order to
469
+ monkey patching a class, we could simply inject what we really want to
470
+ fix the internal stuffs in the broken libraries we're using. Jump to
471
+ _Muack as a mocky patching library_ section for more detail.
472
+
473
+ Here's an quick example:
312
474
 
313
475
  ``` ruby
314
- stub(object).foo{ |&block| block.call(1, 2, 3) }
315
- object.foo {|*args| args } # [1, 2, 3]
476
+ any_instance_of(Array) do |array|
477
+ p array.class # Muack::AnyInstanceOf
478
+ mock(array).name.returns(:instance_exec => true){ inspect }
479
+ end
480
+ p [0, 1].name # '[0, 1]'
481
+ p Muack.verify # true
316
482
  ```
317
483
 
318
- #### [Expecting method to be called with exact argument list](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-with-exact-argument-list)
484
+ Note that this `:instance_exec` option also applies to other modifiers which
485
+ accepts a block for its implementation, i.e. `peek_args` and `peek_return`.
486
+
487
+ #### peek_args
319
488
 
320
- Muack is strict, you always have to specify the argument list.
489
+ What if we don't really want to change an underlying implementation for a
490
+ given method, but we just want to slightly change the arguments, or we
491
+ might just want to take a look at the arguments? Here's an example using
492
+ `peek_args` to modify the original arguments.
493
+
494
+ Note that here we use the proxy mode for the mock, because if we're defining
495
+ our own behaviour, then we already have full control of the arguments.
496
+ There's no points to use both. This also applies to `peek_return`.
321
497
 
322
498
  ``` ruby
323
- mock(object).foo(1, 2)
324
- object.foo(1, 2) # ok
325
- object.foo(3) # fails
499
+ str = 'ff'
500
+ mock(str).to_i.with_any_args.peek_args{ |radix| radix * 2 }
501
+ p str.to_i(8) # 255
502
+ p Muack.verify # true
326
503
  ```
327
504
 
328
- Passing no arguments really means passing no arguments.
505
+ `peek_args` also supports `:instance_exec` mode. Here's an example:
329
506
 
330
507
  ``` ruby
331
- stub(object).foo
332
- stub(object).foo(1, 2)
333
- object.foo(1, 2) # ok
334
- object.foo # ok
335
- object.foo(3) # fails
508
+ any_instance_of(Array) do |array|
509
+ stub(array).push.with_any_args.
510
+ peek_args(:instance_exec => true){ |_| size }
511
+ end
512
+ a = []
513
+ p a.push.dup # [0]
514
+ p a.push.dup # [0, 1]
515
+ p a.push.dup # [0, 1, 2]
516
+ p Muack.verify # true
336
517
  ```
337
518
 
338
- #### [Expecting method to be called with any arguments](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-with-any-arguments)
519
+ We could also omit `|_|` if we don't care about the original argument
520
+ in the above example.
339
521
 
340
- Muack also provides `with_any_args` if we don't really care.
522
+ #### peek_return
523
+
524
+ What if we don't really want to change an underlying implementation for a
525
+ given method, but we just want to slightly change the return value, or we
526
+ might just want to take a look at the return? Here's an example using
527
+ `peek_return` to modify the original return value.
341
528
 
342
529
  ``` ruby
343
- stub(object).foo.with_any_args
344
- object.foo # ok
345
- object.foo(1) # also ok
346
- object.foo(1, 2) # also ok
347
- # ... you get the idea
530
+ str = 'ff'
531
+ mock(str).to_i.with_any_args.peek_return{ |int| int * 2 }
532
+ p str.to_i(16) # 510
533
+ p Muack.verify # true
348
534
  ```
349
535
 
350
- #### [Expecting method to be called with no arguments](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-with-no-arguments)
351
-
352
- Just don't pass any argument :)
536
+ `peek_return` also supports `:instance_exec` mode. Here's an example:
353
537
 
354
538
  ``` ruby
355
- stub(object).foo
356
- object.foo # ok
357
- object.foo(1) # fails
539
+ any_instance_of(Array) do |array|
540
+ stub(array).push.with_any_args.
541
+ peek_return(:instance_exec => true){ |_| size }
542
+ end
543
+ a = []
544
+ p a.push(0) # 1
545
+ p a.push(0) # 2
546
+ p a.push(0) # 3
547
+ p a # [0, 0, 0]
548
+ p Muack.verify # true
358
549
  ```
359
550
 
360
- #### [Expecting method to never be called](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-never-be-called)
551
+ We could also omit `|_|` if we don't care about the original return value
552
+ in the above example.
553
+
554
+ ### Arguments Verifiers (Satisfy)
555
+
556
+ If we're not passing any arguments to the injected method we define, then
557
+ basically we're saying that there's no arguments should be passed to the
558
+ method. If we don't care about the arguments, then we should use
559
+ `with_any_args` modifier. If we want the *exact* arguments, then we
560
+ should just pass the arguments, which would be checked with `==` operator.
361
561
 
362
- Simply use `times(0)`.
562
+ Here's an example:
363
563
 
364
564
  ``` ruby
365
- mock(object).foo.times(0)
366
- object.foo # fails
565
+ obj = Object.new
566
+ mock(obj).say('Hi'){ |arg| arg }
567
+ p obj.say('Hi') # 'Hi'
568
+ p Muack.verify # true
367
569
  ```
368
570
 
369
- Multiple mock with different argument set is fine, too.
571
+ This also applies to multiple arguments:
370
572
 
371
573
  ``` ruby
372
- mock(object).foo(1, 2).times(0)
373
- mock(object).foo(3, 4)
374
- object.foo(3, 4) # ok
375
- object.foo(1, 2) # fails
574
+ obj = Object.new
575
+ mock(obj).say('Hello', 'World'){ |*args| args.join(', ') }
576
+ p obj.say('Hello', 'World') # 'Hello, World'
577
+ p Muack.verify # true
376
578
  ```
377
579
 
378
- #### [Expecting method to be called only once](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-only-once)
580
+ We could also retrieve the block argument:
379
581
 
380
- By default, a mock only expects a call. Using `times(1)` is actually a no-op.
582
+ ``` ruby
583
+ obj = Object.new
584
+ mock(obj).say{ |&block| block.call('Hi') }
585
+ obj.say{ |msg| p msg } # 'Hi'
586
+ p Muack.verify # true
587
+ ```
588
+
589
+ Moreover, we could also have stubs on the same method for different
590
+ arguments. We could think of this as a sort of pattern matching, and Muack
591
+ would try to find the best matched stub for us.
381
592
 
382
593
  ``` ruby
383
- mock(object).foo.times(1)
384
- object.foo
385
- object.foo # fails
594
+ obj = Object.new
595
+ stub(obj).find(0){ 0 }
596
+ stub(obj).find(1){ 1 }
597
+ p obj.find(1) # 1
598
+ p obj.find(0) # 0
599
+ p Muack.verify # true
386
600
  ```
387
601
 
388
- #### [Expecting method to called exact number of times](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-called-exact-number-of-times)
602
+ If `obj.find(2)` is called and Muack cannot find a matched stub, it would
603
+ raise a `Muack::Unexpected` and list the candidates for us.
604
+
605
+ However, What if we don't want to be so exact? Then we should use verifiers.
606
+ We'll introduce each of them in next section. Note that verifiers
607
+ are not recursive though. If you need complex argument verification,
608
+ you'll need to use `satisfy` verifier which you could give an arbitrary
609
+ block to verify anything.
389
610
 
390
- Times! Which is the same as RR.
611
+ #### is_a
612
+
613
+ `is_a` would check if the argument is a kind of the given class.
614
+ Actually, it's calling `kind_of?` underneath.
391
615
 
392
616
  ``` ruby
393
- mock(object).foo.times(3)
394
- object.foo
395
- object.foo
396
- object.foo
397
- object.foo # fails
617
+ obj = Object.new
618
+ mock(obj).say(is_a(String)){ |arg| arg }
619
+ p obj.say('something') # 'something'
620
+ p Muack.verify # true
398
621
  ```
399
622
 
400
- Alternatively, you could also do this. It's exactly the same.
623
+ #### anything
624
+
625
+ `anything` is a wildcard argument verifier. It matches anything.
626
+ Although this actually verifies nothing, we could still think of
627
+ this as an arity verifier. Since one anything is not two anythings.
401
628
 
402
629
  ``` ruby
403
- 3.times{ mock(object).foo }
404
- object.foo
405
- object.foo
406
- object.foo
407
- object.foo # fails
630
+ obj = Object.new
631
+ mock(obj).say(anything){ |arg| arg }.times(2)
632
+ p obj.say(0) # 0
633
+ p obj.say(true) # true
634
+ p Muack.verify # true
408
635
  ```
409
636
 
410
- #### [Expecting method to be called minimum number of times](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-minimum-number-of-times)
637
+ #### match
411
638
 
412
- It's not supported in Muack, but we could emulate it somehow:
639
+ `match` would check the argument with `match` method. Usually this is
640
+ used with regular expression, but anything which responds to `match`
641
+ should work.
413
642
 
414
643
  ``` ruby
415
- times = 0
416
- stub(object).foo{ times += 1 }
417
- object.foo
418
- object.foo
419
- raise "BOOM" if times <= 3
644
+ obj = Object.new
645
+ mock(obj).say(match(/\w+/)){ |arg| arg }
646
+ p obj.say('Hi') # 'Hi'
647
+ p Muack.verify # true
420
648
  ```
421
649
 
422
- #### [Expecting method to be called maximum number of times](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-maximum-number-of-times)
650
+ Note that please don't pass the regular expression directly without
651
+ wrapping it with a match verifier, or how do we distinguish if we
652
+ really want to make sure the argument is exactly the regular expression?
423
653
 
654
+ #### hash_including
424
655
 
425
- It's not supported in Muack, but we could emulate it somehow:
656
+ `hash_including` would check if the given hash is the actual
657
+ argument's subset.
426
658
 
427
659
  ``` ruby
428
- times = 0
429
- stub(object).foo{ times += 1; raise "BOOM" if times > 3 }
430
- object.foo
431
- object.foo
432
- object.foo
433
- object.foo
660
+ obj = Object.new
661
+ mock(obj).say(hash_including(:a => 0)){ |arg| arg }
662
+ p obj.say(:a => 0, :b => 1) # {:a => 0, :b => 1}
663
+ p Muack.verify # true
434
664
  ```
435
665
 
436
- #### [Expecting method to be called any number of times](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#expecting-method-to-be-called-any-number-of-times)
666
+ #### including
437
667
 
438
- Just use `stub`, which is exactly why it is designed.
668
+ `including` would check if the actual argument includes the given value
669
+ via `include?` method.
439
670
 
440
671
  ``` ruby
441
- stub(object).foo
442
- object.foo
443
- object.foo
444
- object.foo
672
+ obj = Object.new
673
+ mock(obj).say(including(0)){ |arg| arg }
674
+ p obj.say([0,1]) # [0,1]
675
+ p Muack.verify # true
445
676
  ```
446
677
 
447
- #### [Argument wildcard matchers](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#argument-wildcard-matchers)
678
+ #### within
448
679
 
449
- `anything` is the same as RR.
680
+ `within` is the reverse version of `including`, verifying if the actual
681
+ argument is included in the given value.
450
682
 
451
683
  ``` ruby
452
- mock(object).foobar(1, anything)
453
- object.foobar(1, :my_symbol)
684
+ obj = Object.new
685
+ mock(obj).say(within([0, 1])){ |arg| arg }
686
+ p obj.say(0) # 0
687
+ p Muack.verify # true
454
688
  ```
455
689
 
456
- `is_a` is the same as RR.
690
+ #### respond_to
691
+
692
+ `respond_to` would check if the actual argument would be responding to
693
+ the given message, checked via `respond_to?`, also known as duck typing.
457
694
 
458
695
  ``` ruby
459
- mock(object).foobar(is_a(Time))
460
- object.foobar(Time.now)
696
+ obj = Object.new
697
+ mock(obj).say(respond_to(:size)){ |arg| arg }
698
+ p obj.say([]) # []
699
+ p Muack.verify # true
461
700
  ```
462
701
 
463
- No numeric supports. Simply use `is_a(Numeric)`
702
+ Note that you could give multiple messages to `respond_to`.
464
703
 
465
704
  ``` ruby
466
- mock(object).foobar(is_a(Numeric))
467
- object.foobar(99)
705
+ obj = Object.new
706
+ mock(obj).say(respond_to(:size, :reverse)){ |arg| arg }
707
+ p obj.say([]) # []
708
+ p Muack.verify # true
468
709
  ```
469
710
 
470
- No boolean supports, but you can use union (`|`).
711
+ #### satisfy
712
+
713
+ `satisfy` accepts a block to let you do arbitrary verification.
714
+ nil and false are considered false, otherwise true, just like in
715
+ regular if expression.
471
716
 
472
717
  ``` ruby
473
- mock(object).foobar(is_a(TrueClass) | is_a(FalseClass))
474
- object.foobar(false)
718
+ obj = Object.new
719
+ mock(obj).say(satisfy{ |arg| arg % 2 == 0 }){ |arg| arg }
720
+ p obj.say(0) # 0
721
+ p Muack.verify # true
475
722
  ```
476
723
 
477
- Since duck_type is a weird name to me. Here we use `respond_to(:walk, :talk)`.
724
+ #### Disjunction (|)
725
+
726
+ If what we want is the actual argument be within either `0..1` or `3..4`?
727
+ We don't really have to use `satisfy` to build custom verifier, we could
728
+ compose verifiers with disjunction operator (|).
478
729
 
479
730
  ``` ruby
480
- mock(object).foobar(respond_to(:walk, :talk))
481
- arg = Object.new
482
- def arg.walk; 'waddle'; end
483
- def arg.talk; 'quack'; end
484
- object.foobar(arg)
731
+ obj = Object.new
732
+ mock(obj).say(within(0..1) | within(3..4)){ |arg| arg }.times(2)
733
+ p obj.say(0) # 0
734
+ p obj.say(4) # 4
735
+ p Muack.verify # true
485
736
  ```
486
737
 
487
- You can also use intersection (`&`) for multiple responses.
488
- Though there's not much point here. Just want to demonstrate.
738
+ Or boolean, you might say:
489
739
 
490
740
  ``` ruby
491
- mock(object).foobar(respond_to(:walk) & respond_to(:talk))
492
- arg = Object.new
493
- def arg.walk; 'waddle'; end
494
- def arg.talk; 'quack'; end
495
- object.foobar(arg)
741
+ obj = Object.new
742
+ mock(obj).say(is_a(TrueClass) | is_a(FalseClass)){ |arg| arg }.times(2)
743
+ p obj.say(true) # true
744
+ p obj.say(false) # false
745
+ p Muack.verify # true
496
746
  ```
497
747
 
498
- Don't pass ranges directly for ranges, use `within`. Or how do we tell
499
- if we really want the argument to be a `Range` object?
748
+ #### Conjunction (&)
749
+
750
+ If what we want is the actual argument not only a kind of something,
751
+ but also responds to something. For example, an Enumerable requires the
752
+ class implements each method. We could use conjunction for this.
500
753
 
501
754
  ``` ruby
502
- mock(object).foobar(within(1..10))
503
- object.foobar(5)
755
+ obj = Object.new
756
+ mock(obj).say(is_a(Enumerable) & respond_to(:each)){}.times(3)
757
+ p obj.say( [] ) # nil
758
+ p obj.say( {} ) # nil
759
+ p obj.say(0..1) # nil
760
+ p Muack.verify # true
504
761
  ```
505
762
 
506
- The same goes to regular expression. Use `match` instead.
763
+ ### Caveat
764
+
765
+ #### Mixing mocks and stubs
766
+
767
+ We could and probably would also want to mix mocks and stubs, for example,
768
+ we might be concerned about some methods for a given object, but not the
769
+ other methods.
507
770
 
508
771
  ``` ruby
509
- mock(object).foobar(match(/on/))
510
- object.foobar("ruby on rails")
772
+ obj = Object.new
773
+ stub(obj).name{ 'obj' }
774
+ mock(obj).id { 12345 }
775
+ p obj.name # 'obj'
776
+ p obj.name # 'obj'
777
+ p obj.id # 12345
778
+ p Muack.verify # true
511
779
  ```
512
780
 
513
- `hash_including` is the same as RR.
781
+ However, it might act unexpectedly if we mock and stub on the same object
782
+ for the same method. It would somehow act like the latter would always win!
783
+ So if we define mock later for the same method, previously defined stub
784
+ would never be called. On the other hand, if we define stub later for the
785
+ same method, previously defined mock would always complain because it would
786
+ never be called, either!
787
+
788
+ This does not mean previously defined mocks or stubs get overwritten, because
789
+ it would still take effect. It's just that there's no way they could get
790
+ called. So this is mostly not desired.
791
+
792
+ The ideal solution to this would be raising an error immediately, or really
793
+ make it could be overwritten. However I didn't find a good way to handle this
794
+ without rewriting the internal details. So I'll just leave it as it is,
795
+ and hope no one would ever try to do this.
796
+
797
+ #### any_instance_of shares all calls for a given class
798
+
799
+ We might assume that mocks with any_instance_of would work exactly the same
800
+ as regular mocks, but this is actually not the case. Regular mocks count
801
+ on every individual instance, but all instances share the same count for
802
+ any_instance_of.
803
+
804
+ With one instance:
514
805
 
515
806
  ``` ruby
516
- mock(object).foobar(hash_including(:red => "#FF0000", :blue => "#0000FF"))
517
- object.foobar({:red => "#FF0000", :blue => "#0000FF", :green => "#00FF00"})
807
+ any_instance_of(Array){ |array| mock(array).f{true}.times(2) }
808
+ a = []
809
+ p a.f # true
810
+ p a.f # true
811
+ p Muack.verify # true
518
812
  ```
519
813
 
520
- `satisfy` is the same as RR.
814
+ With two instances:
521
815
 
522
816
  ``` ruby
523
- mock(object).foobar(satisfy {|arg| arg.length == 2 })
524
- object.foobar("xy")
817
+ any_instance_of(Array){ |array| mock(array).f{true}.times(2) }
818
+ p [].f # true
819
+ p [].f # true
820
+ p Muack.verify # true
821
+ ```
822
+
823
+ So remember to count on all instances, but not individual ones.
824
+
825
+ ### Extra Topics
826
+
827
+ #### Muack as a mocky patching library
828
+
829
+ Consider you're using a broken library and you need an immediate fix without
830
+ waiting for upstream to merge your patch, and release a new version.
831
+
832
+ You could fix it more elegantly by subclassing the original class, or try to
833
+ include or extend a module to make the original class work correctly. But
834
+ sometimes we just cannot do this because of the implementation. They might
835
+ not be extensible at all. Consider if there's a method contains 1,000
836
+ lines... There's no way to change it in the middle of the method other than
837
+ touching the lines directly, unless we have some line based AOP tools...
838
+ which is not really practical.
839
+
840
+ In this case, we could fork it and maintain everything by ourselves, and
841
+ merge from upstream occasionally. However we might only want to do this as
842
+ the last resort since this could cost a lot.
843
+
844
+ Alternatively, we can copy the original code, and put it somewhere, and
845
+ load it after the original code was loaded, so we have the patched and
846
+ correct code running. This is also called monkey patching, patching like a
847
+ monkey. Generally this is a bad idea, but sometimes we can only do this to
848
+ workaround some broken libraries. For example, some libraries might not be
849
+ maintained, or the authors refused to fix this due to other reasonable or
850
+ unreasonable reason.
851
+
852
+ The most notable drawback of monkey patching is that, we're copying a lot of
853
+ codes which could be changed upstream, and we might not be aware of that,
854
+ and update our monkey patch accordingly. This could cause some incompatible
855
+ issues.
856
+
857
+ That means, the fewer copied codes, the better. Muack could actually help
858
+ in this case. I called this mocky patching. The advantage of using this
859
+ technique is that, we have `peek_args` and `peek_return` which we could
860
+ modify the arguments or return values in runtime, without changing any
861
+ implementation of a particular method.
862
+
863
+ Here's a real world example with rails_admin. The problem in rails_admin is
864
+ that, it assumes every associated records should have already been saved,
865
+ thus having an id, and there's also a particular show page for it.
866
+
867
+ However, in our application, we could have associated records not yet saved
868
+ in the database. rails_admin would try to retrieve routes for those unsaved
869
+ records, and rails would raise RoutingError because rails_admin is passing
870
+ no id for a show path.
871
+
872
+ The idea of this fix is simple. Just don't try to get the show page for
873
+ records which are not yet saved, i.e. records without an id. However this
874
+ is actually extremely hard to fix in rails_admin without monkey patching!
875
+
876
+ I'll skip all those details and my rants. In the end, I fixed this by
877
+ trying to peek the arguments for a particular method, and if and only if
878
+ the passed records are not yet saved in the database, we fake the arguments.
879
+ Otherwise, we just bypass and fallback to the original implementation.
880
+
881
+ Here's the code:
882
+
883
+ ``` ruby
884
+ Muack::API.stub(RailsAdmin::Config::Actions).find.with_any_args.
885
+ peek_args do |*args|
886
+ custom_key, bindings = args
887
+ if bindings && bindings[:object] && bindings[:object].id.nil?
888
+ [nil, {}] # There's no show page for unsaved records
889
+ else
890
+ args # Bypass arguments
891
+ end
892
+ end
893
+ ```
894
+
895
+ If we don't do mocky patching but monkey patching, we'll end up with
896
+ copying the entire method for RailsAdmin::Config::Actions.find, which then,
897
+ we'll be responsible for updating this method if some of the original
898
+ implementation changed.
899
+
900
+ Note that in mocky patching, we should always use stub and never call
901
+ `Muack.verify` or `Muack.reset`, or that would defeat the purpose of
902
+ mocky patching.
903
+
904
+ #### Muack as a development runtime static typing system
905
+
906
+ Ever consider a static type system in Ruby? You could actually see a lot of
907
+ asserts inserted in the beginning of some methods in some libraries. For
908
+ example, there are `assert_valid_key_size`, `assert_kind_of`, etc, in
909
+ [dm-core][], and `assert_valid_keys`, `assert_valid_transaction_action`,
910
+ and various random asserts in activerecord.
911
+
912
+ You could find them by searching against `raise ArgumentError` because
913
+ rails is much less consistent and sometimes it's hard to find a pattern in
914
+ rails. But you get the idea, those `ArgumentError` would much help us debug
915
+ our code from misusing the API, and that's exactly the point of type system,
916
+ or more specifically, static type system.
917
+
918
+ We could also use some static analysis tools to do something like this, for
919
+ example, there's [ruby-lint][]. However, as you might already know, since
920
+ Ruby is so dynamic, static analysis tools cannot really do a great job if
921
+ our code is quite dynamic. Of course we could write it more statically,
922
+ and treat our static analysis tools better, but that might not be the spirit
923
+ of Ruby somehow.
924
+
925
+ Alternatively, it would be great to do this static type checking
926
+ dynamically... I mean, in the runtime rather than compile time. This
927
+ means it would be much more accurate, just like those asserts in the
928
+ above examples.
929
+
930
+ However, if we're doing those checks in a hot path, for example, right
931
+ inside a loop looping over a million times, this would definitely slow
932
+ things down if we're checking them in the runtime. Even if we put `$DEBUG`
933
+ guards around those check, we're still suffering from checking the flag.
934
+
935
+ It would be great if we could actually just remove those checks in
936
+ production, while turn it on when we're developing or debugging.
937
+ Muack could actually fulfill this desire, as it could inject codes
938
+ externally and seamlessly, and we could remove them anytime when we
939
+ call `Muack.reset`, or, simply don't do any stubs in production config.
940
+
941
+ Consider we have two classes:
942
+
943
+ ``` ruby
944
+ Food = Class.new
945
+ User = Class.new{ attr_accessor :food }
946
+ ```
947
+
948
+ And we could make sure User#food is always a kind of `Food` by putting this
949
+ into a development config or so:
950
+
951
+ ``` ruby
952
+ Muack::API.module_eval do
953
+ any_instance_of(User) do |user|
954
+ stub(user).food = is_a(Food)
955
+ end
956
+ end
525
957
  ```
526
958
 
527
- #### [Writing your own argument matchers](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#writing-your-own-argument-matchers)
959
+ And then if we're trying to set a food other than a `Food`...
960
+
961
+ ``` ruby
962
+ u, f = User.new, Food.new
963
+ u.food = f # ok
964
+ u.food = 1 # raise Muack::Unexpected
965
+ ```
528
966
 
529
- See [`lib/muack.rb`][muack.rb] and [`lib/muack/satisfy.rb`][satisfy.rb],
530
- you would get the idea soon. Here's how `is_a` implemented.
967
+ This could go wild and we could customize our own domain specific argument
968
+ verifiers. For example, we could do this to check if the food is frozen:
531
969
 
532
970
  ``` ruby
533
- module Muack::API
534
- module_function
535
- def is_a klass
536
- Muack::IsA.new(klass)
971
+ Food = Class.new
972
+ User = Class.new{ attr_accessor :food }
973
+
974
+ FoodFrozen = Class.new(Muack::Satisfy) do
975
+ def initialize
976
+ super lambda{ |actual_arg| actual_arg.frozen? }
537
977
  end
538
978
  end
539
979
 
540
- class Muack::IsA < Muack::Satisfy
541
- def initialize klass
542
- super lambda{ |actual_arg| actual_arg.kind_of?(klass) }, [klass]
980
+ Muack::API.module_eval do
981
+ any_instance_of(User) do |user|
982
+ stub(user).food = FoodFrozen.new
543
983
  end
544
984
  end
985
+
986
+ u = User.new
987
+ p u.food = Food.new.freeze # ok
988
+ p u.food = Food.new # raise Muack::Unexpected
545
989
  ```
546
990
 
547
- [muack.rb]: https://github.com/godfat/muack/blob/master/lib/muack.rb
548
- [satisfy.rb]: https://github.com/godfat/muack/blob/master/lib/muack/satisfy.rb
991
+ Please check _Arguments Verifiers (Satisfy)_ section for more argument
992
+ verifiers details.
993
+
994
+ [dm-core]: https://github.com/datamapper/dm-core
995
+ [ruby-lint]: https://github.com/YorickPeterse/ruby-lint
549
996
 
550
997
  ## USERS:
551
998
 
@@ -561,7 +1008,7 @@ end
561
1008
 
562
1009
  Apache License 2.0
563
1010
 
564
- Copyright (c) 2013, Lin Jen-Shin (godfat)
1011
+ Copyright (c) 2013~2014, Lin Jen-Shin (godfat)
565
1012
 
566
1013
  Licensed under the Apache License, Version 2.0 (the "License");
567
1014
  you may not use this file except in compliance with the License.