minispec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.pryrc +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +2140 -0
  6. data/Rakefile +11 -0
  7. data/bin/minispec +4 -0
  8. data/lib/minispec.rb +175 -0
  9. data/lib/minispec/api.rb +2 -0
  10. data/lib/minispec/api/class.rb +195 -0
  11. data/lib/minispec/api/class/after.rb +49 -0
  12. data/lib/minispec/api/class/around.rb +54 -0
  13. data/lib/minispec/api/class/before.rb +101 -0
  14. data/lib/minispec/api/class/helpers.rb +116 -0
  15. data/lib/minispec/api/class/let.rb +44 -0
  16. data/lib/minispec/api/class/tests.rb +33 -0
  17. data/lib/minispec/api/instance.rb +158 -0
  18. data/lib/minispec/api/instance/mocks/doubles.rb +36 -0
  19. data/lib/minispec/api/instance/mocks/mocks.rb +319 -0
  20. data/lib/minispec/api/instance/mocks/spies.rb +17 -0
  21. data/lib/minispec/api/instance/mocks/stubs.rb +105 -0
  22. data/lib/minispec/helpers.rb +1 -0
  23. data/lib/minispec/helpers/array.rb +56 -0
  24. data/lib/minispec/helpers/booleans.rb +108 -0
  25. data/lib/minispec/helpers/generic.rb +24 -0
  26. data/lib/minispec/helpers/mocks/expectations.rb +29 -0
  27. data/lib/minispec/helpers/mocks/spies.rb +36 -0
  28. data/lib/minispec/helpers/raise.rb +44 -0
  29. data/lib/minispec/helpers/throw.rb +29 -0
  30. data/lib/minispec/mocks.rb +11 -0
  31. data/lib/minispec/mocks/expectations.rb +77 -0
  32. data/lib/minispec/mocks/stubs.rb +178 -0
  33. data/lib/minispec/mocks/validations.rb +80 -0
  34. data/lib/minispec/mocks/validations/amount.rb +63 -0
  35. data/lib/minispec/mocks/validations/arguments.rb +161 -0
  36. data/lib/minispec/mocks/validations/caller.rb +43 -0
  37. data/lib/minispec/mocks/validations/order.rb +47 -0
  38. data/lib/minispec/mocks/validations/raise.rb +111 -0
  39. data/lib/minispec/mocks/validations/return.rb +74 -0
  40. data/lib/minispec/mocks/validations/throw.rb +91 -0
  41. data/lib/minispec/mocks/validations/yield.rb +141 -0
  42. data/lib/minispec/proxy.rb +201 -0
  43. data/lib/minispec/reporter.rb +185 -0
  44. data/lib/minispec/utils.rb +139 -0
  45. data/lib/minispec/utils/differ.rb +325 -0
  46. data/lib/minispec/utils/pretty_print.rb +51 -0
  47. data/lib/minispec/utils/raise.rb +123 -0
  48. data/lib/minispec/utils/throw.rb +140 -0
  49. data/minispec.gemspec +27 -0
  50. data/test/mocks/expectations/amount.rb +67 -0
  51. data/test/mocks/expectations/arguments.rb +126 -0
  52. data/test/mocks/expectations/caller.rb +55 -0
  53. data/test/mocks/expectations/generic.rb +35 -0
  54. data/test/mocks/expectations/order.rb +46 -0
  55. data/test/mocks/expectations/raise.rb +166 -0
  56. data/test/mocks/expectations/return.rb +71 -0
  57. data/test/mocks/expectations/throw.rb +113 -0
  58. data/test/mocks/expectations/yield.rb +109 -0
  59. data/test/mocks/spies/amount.rb +68 -0
  60. data/test/mocks/spies/arguments.rb +57 -0
  61. data/test/mocks/spies/generic.rb +61 -0
  62. data/test/mocks/spies/order.rb +38 -0
  63. data/test/mocks/spies/raise.rb +158 -0
  64. data/test/mocks/spies/return.rb +71 -0
  65. data/test/mocks/spies/throw.rb +113 -0
  66. data/test/mocks/spies/yield.rb +101 -0
  67. data/test/mocks/test__doubles.rb +98 -0
  68. data/test/mocks/test__expectations.rb +27 -0
  69. data/test/mocks/test__mocks.rb +197 -0
  70. data/test/mocks/test__proxies.rb +61 -0
  71. data/test/mocks/test__spies.rb +43 -0
  72. data/test/mocks/test__stubs.rb +427 -0
  73. data/test/proxified_asserts.rb +34 -0
  74. data/test/setup.rb +53 -0
  75. data/test/test__around.rb +58 -0
  76. data/test/test__assert.rb +510 -0
  77. data/test/test__before_and_after.rb +117 -0
  78. data/test/test__before_and_after_all.rb +71 -0
  79. data/test/test__helpers.rb +197 -0
  80. data/test/test__raise.rb +104 -0
  81. data/test/test__skip.rb +41 -0
  82. data/test/test__throw.rb +103 -0
  83. metadata +196 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2973847800ef2aac6fb86844541a766d114aa04c
4
+ data.tar.gz: 3674b9c2392225cdd50768fd13de39563534c583
5
+ SHA512:
6
+ metadata.gz: 6eb0c4d0ca16fff824fa9d249baf8a9cf14c166dee42955a3bc9242b5843922d2ee9547eda4a40781dead96775f51223f1f939fac9d01abda2f0a2b94fe97bf9
7
+ data.tar.gz: 610910d94d916f251c542c2f470e6b3c52650a913311cf6d5f79285350c1083b60ce083bdab08991b63e5ab7e4a1feed54b91f12bb98738416bc4d921086a90f
data/.pryrc ADDED
@@ -0,0 +1,2 @@
1
+ $:.unshift './lib'
2
+ require 'minispec'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in minispec.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Slee Woo <mail@sleewoo.com>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2140 @@
1
+
2
+ ### Minispec
3
+
4
+ **Simple, Intuitive, Full-featured Testing Framework**
5
+
6
+ [Install](#install) |
7
+ [Quick Start](#quick-start) |
8
+ [Docs](#docs) |
9
+ [Contributors](#contributors) |
10
+ [Authors and License](#license)
11
+
12
+ ## What and Why
13
+
14
+ Simply i tired of syntax like `assert_equal(b, a)`, `a.should == b` and `expect(a).to eq(b)` etc.
15
+
16
+ `is(a) == b` is all i want to type.
17
+
18
+ Also i tired to learn framework specific techniques. I want simply to use Ruby's native methods:
19
+
20
+ ```ruby
21
+ is(a) == b
22
+ does(a).include?(b)
23
+ is(a).empty?
24
+ ```
25
+
26
+ `==`, `include?`, `empty?` are all Ruby methods called on `a`.
27
+
28
+ What you see around `a` is a simple wrapper that passes messages to `a` and mark the assertion as passed or failed, depending on returned value.
29
+
30
+
31
+ ## Install
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'minispec'
37
+ ```
38
+
39
+ And then execute:
40
+
41
+ ```bash
42
+ $ bundle
43
+ ```
44
+
45
+ Or install it yourself as:
46
+
47
+ ```bash
48
+ $ gem install minispec
49
+ ```
50
+
51
+ then load it using `require 'minispec'`
52
+
53
+
54
+ ## Quick Start
55
+
56
+ *Examples borrowed from [github.com/rubyspec](https://github.com/rubyspec/rubyspec). Commented are the original assertions.*
57
+
58
+ ```ruby
59
+ describe "Array.allocate" do
60
+ it "returns an instance of Array" do
61
+ ary = Array.allocate
62
+ # ary.should be_an_instance_of(Array)
63
+ is(ary).instance_of?(Array)
64
+ end
65
+
66
+ it "returns a fully-formed instance of Array" do
67
+ ary = Array.allocate
68
+ # ary.size.should == 0
69
+ assert(ary.size) == 0
70
+ ary << 1
71
+ # ary.should == [1]
72
+ assert(ary) == [1]
73
+ end
74
+
75
+ it "does not accept any arguments" do
76
+ # lambda { Array.allocate(1) }.should raise_error(ArgumentError)
77
+ does { Array.allocate(1) }.raise?(ArgumentError)
78
+ end
79
+ end
80
+ ```
81
+
82
+
83
+ # Docs
84
+
85
+ * [Defining Specs](#defining-specs)
86
+ * [Nested Specs](#-nested-specs)
87
+ * [Defining Tests](#defining-tests)
88
+ * [Skipping a test](#-skipping-a-test)
89
+ * [Mark a test as failed](#-mark-a-test-as-failed)
90
+ * [Shared examples and setups](#shared-examples-and-setups)
91
+ * [Resetting included resources](#-resetting-included-resources)
92
+ * [Local variables and subject](#local-variables-and-subject)
93
+ * [Custom error messages](#custom-error-messages)
94
+ * [Hooks](#hooks)
95
+ * [`before` and `before_all`](#-before-and-before_all)
96
+ * [`after` and `after_all`](#-after-and-after_all)
97
+ * [`around` and `around_all`](#-around-and-around_all)
98
+ * [Filters](#-filters)
99
+ * [Assertions](#assertions)
100
+ * [Negative assertions](#-negative-assertions)
101
+ * [Semantic sugar](#-semantic-sugar)
102
+ * [Helpers](#helpers)
103
+ * [Built-in helpers](#-built-in-helpers)
104
+ * [raise](#-raise-helper)
105
+ * [throw](#-throw-helper)
106
+ * [boolean](#-boolean-helpers)
107
+ * [array](#-array-helpers)
108
+ * [silent](#-silent-helper)
109
+ * [Custom helpers](#-custom-helpers)
110
+ * [Helpers with blocks](#-helpers-with-blocks)
111
+ * [Helpers aliases](#-helpers-aliases)
112
+ * [Mocking](#mocking)
113
+ * [Expectations](#-expectations)
114
+ * [Constraints](#-constraints)
115
+ * [arguments](#-arguments)
116
+ * [returned value](#-returned-value)
117
+ * [raised exception](#-raised-exception)
118
+ * [thrown symbol](#-thrown-symbol)
119
+ * [yielded arguments](#-yielded-arguments)
120
+ * [messages amount](#-messages-amount)
121
+ * [messages order](#-messages-order)
122
+ * [Spies](#spies)
123
+ * [Stubs](#stubs)
124
+ * [Argument-vary stubs](#-argument-vary-stubs)
125
+ * [Stubbing multiple methods at once](#-stubbing-multiple-methods-at-once)
126
+ * [Chained stubs](#-chained-stubs)
127
+ * [Calling original](#-calling-original)
128
+ * [Stubs visibility](#-stubs-visibility)
129
+ * [Mocks](#mocks)
130
+ * [Doubles](#doubles)
131
+ * [Running Specs](#running-specs)
132
+
133
+
134
+ ## Defining Specs
135
+
136
+ There are 2 ways to define Minispec specs: by using Minispec's DSL and by using Ruby classes.
137
+
138
+ Minispec's DSL has 3 methods that allow to define specs:
139
+
140
+ * `describe`
141
+ * `context`
142
+ * `section`
143
+
144
+ They accepts a single argument(the spec name) and a block containing setups and tests:
145
+
146
+ ```ruby
147
+ describe SomeClass do
148
+ # setups and tests
149
+ end
150
+ ```
151
+
152
+ When using classes you should `include Minispec`:
153
+
154
+ ```ruby
155
+ class SomeSpec
156
+ include Minispec
157
+ # setups and tests
158
+ end
159
+ ```
160
+
161
+ [&#8679; Table of Contents](#docs)
162
+
163
+
164
+ ### &#8627; Nested Specs
165
+
166
+ Minispec allows to define unlimitedly nested specs using same `describe`/`context`/`section` DSL:
167
+
168
+ Nested specs are akin of valve, they inherits everything(except tests) from parent spec but share nothing with it and does not change parent state in any way:
169
+
170
+ ```ruby
171
+ describe :A do
172
+ before { @letter = 'A' }
173
+
174
+ # some tests
175
+
176
+ describe :B do
177
+ before { @letter = 'B' }
178
+ # it will run both inherited and own callbacks but wont change A's @letter
179
+ end
180
+
181
+ # @letter is still 'A'
182
+ end
183
+ ```
184
+
185
+
186
+ [&#8679; Table of Contents](#docs)
187
+
188
+
189
+ ## Defining Tests
190
+
191
+ Here are the methods that can be used to define tests:
192
+
193
+ * `test`
194
+ * `testing`
195
+ * `example`
196
+ * `should`
197
+ * `it`
198
+
199
+
200
+ ```ruby
201
+ # spec
202
+ describe :RequestTest do
203
+
204
+ # test 1
205
+ should 'respond to #user_agent' do
206
+ request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
207
+ does(request).respond_to?(:user_agent)
208
+ assert(request.user_agent) == 'Test'
209
+ end
210
+
211
+ # test 2
212
+ it 'is secure when the url scheme is https' do
213
+ request = Sinatra::Request.new('rack.url_scheme' => 'https')
214
+ is(request).secure?
215
+ end
216
+
217
+ # test 3
218
+ testing 'it respects X-Forwarded-Proto header for proxied SSL' do
219
+ request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
220
+ is(request).secure?
221
+ end
222
+
223
+ # test 4
224
+ it "exposes the preferred type's parameters" do
225
+ request = Sinatra::Request.new('HTTP_ACCEPT' => 'image/jpeg; compress=0.2')
226
+ assert(request.preferred_type.params) == { 'compress' => '0.2' }
227
+ end
228
+
229
+ # etc.
230
+ end
231
+ ```
232
+
233
+ Tests can Not be defined inside another tests. If you need concerns separation use nested specs instead.
234
+
235
+ [&#8679; Table of Contents](#docs)
236
+
237
+
238
+ ### &#8627; Skipping a test
239
+
240
+ When you need to skip some test simply use `skip` method inside test:
241
+
242
+ ```ruby
243
+ should 'work with new Hash syntax' do
244
+ skip if RUBY_VERSION < '1.9'
245
+ # code here wont be evaluated on Ruby 1.8
246
+ end
247
+ ```
248
+
249
+ Any code after `skip` method will be just ignored and test reported as skipped.
250
+
251
+
252
+ [&#8679; Table of Contents](#docs)
253
+
254
+
255
+ ### &#8627; Mark a test as failed
256
+
257
+ When you need a custom failure message use `fail` method:
258
+
259
+ ```ruby
260
+ # will generate standard failure message
261
+ assert(1) == 2
262
+
263
+ # using custom failure message
264
+ 1 == 2 || fail('expected 1 to be equal to 2 :(')
265
+ ```
266
+
267
+ Assertions that comes after a failure will be ignored.
268
+
269
+ If you need all assertions to be evaluated regardless failures use `continue_on_failures(true)` at spec level(not inside test).
270
+
271
+
272
+ [&#8679; Table of Contents](#docs)
273
+
274
+
275
+ ## Shared examples and setups
276
+
277
+ Often you need to share some setups and tests(a.k.a examples) between various specs.
278
+
279
+ To define shared setups/examples simply define a module that includes `Minispec`.
280
+
281
+ Later that module can be included into any spec.
282
+
283
+ ```ruby
284
+ module MailboxAssets
285
+ include Minispec
286
+
287
+ before { @mailbox = Mailbox.new }
288
+
289
+ test '#deliver' do
290
+ # ...
291
+ end
292
+ end
293
+
294
+ describe :SMTP do
295
+ include MailboxAssets
296
+
297
+ before do
298
+ # will run included hooks before ones defined here,
299
+ # so @mailbox is available here
300
+ @mailbox.transport = :smtp
301
+ end
302
+
303
+ # will set @mailbox's transport to :smtp and run #deliver test
304
+ end
305
+
306
+ describe :sendmail do
307
+ include MailboxAssets
308
+
309
+ before { @mailbox.transport = :sendmail }
310
+ # will set @mailbox's transport to :sendmail and run #deliver test
311
+ end
312
+ ```
313
+
314
+
315
+ [&#8679; Table of Contents](#docs)
316
+
317
+
318
+ ### &#8627; Resetting included resources
319
+
320
+ Minispec will include following resources from base module:
321
+
322
+ * `:tests`
323
+ * `:helpers`
324
+ * `:before`
325
+ * `:after`
326
+ * `:around`
327
+ * `:vars`
328
+ * `:continue_on_failures`
329
+
330
+ If you need to reset any of included resource, use `reset` method with resources to reset:
331
+
332
+ ```ruby
333
+ module CPUExamples do
334
+ # some setups and tests
335
+ end
336
+
337
+ describe :MacBook do
338
+ include CPUExamples
339
+
340
+ reset :before # resets :before hooks
341
+ reset :before, :after # resets :before and :after hooks
342
+ # etc
343
+
344
+ end
345
+ ```
346
+
347
+
348
+ [&#8679; Table of Contents](#docs)
349
+
350
+
351
+ ## Local variables and subject
352
+
353
+ Minispec provides `let` method as a clean way to define local variables. Its block are executed only once per test, when given variable used for first time.
354
+
355
+ ```ruby
356
+ describe Array do
357
+ let(:array) { Array.new }
358
+
359
+ it 'is a Enumerable' do
360
+ assert(array).is_a? Enumerable
361
+ end
362
+ end
363
+ ```
364
+
365
+ `subject` allow to test some object without repeatedly typing it. It is automatically set when a spec defined using Minispec's DSL:
366
+
367
+ ```ruby
368
+ describe Hash do
369
+ it 'responds to :[]' do
370
+ assert.respond_to? :[]
371
+ end
372
+ end
373
+ ```
374
+
375
+ In example above the subject are automatically set to `Hash` and automatically picked up by `assert`.
376
+
377
+ It can also be written as `assert(subject).respond_to? :[]` or even `assert(Hash).respond_to? :[]`, in case you prefer more explicit assertions.
378
+
379
+ [&#8679; Table of Contents](#docs)
380
+
381
+
382
+ ## Custom error messages
383
+
384
+ Minispec will do its best to provide detailed failure messages.
385
+
386
+ However there are cases when you need to use custom messages.
387
+
388
+ This is easily done by providing your error message as second argument,
389
+ using `:on_error` key:
390
+
391
+ ```ruby
392
+ assert(pizza, on_error: "Seems not enough olives...").is_tasty
393
+ ```
394
+
395
+ Now if pizza is not tasty enough, Minispec will inform us about the lack of olives rather than just generally complain about poor taste.
396
+
397
+
398
+ [&#8679; Table of Contents](#docs)
399
+
400
+
401
+ ## Hooks
402
+
403
+ ### &#8627; `before` and `before_all`
404
+
405
+ `before` callback runs before each test:
406
+
407
+ ```ruby
408
+ describe Array do
409
+ before { @array = subject.new }
410
+ # @array will be different for each test
411
+ end
412
+ ```
413
+
414
+ **Important:** `before` callbacks are incremental, meant that all callbacks, inherited and defined, will be called.
415
+
416
+ First will be called inherited callbacks. Defined ones will run second:
417
+
418
+ ```ruby
419
+ describe :A do
420
+ before { @letter = 'A' }
421
+
422
+ context :a do
423
+ before { @letter.downcase! }
424
+ # two callbacks will run here:
425
+ # 1. @letter = 'A'
426
+ # 2. @letter.downcase!
427
+ end
428
+
429
+ # @letter is still 'A' cause child specs does not change parent's state
430
+ end
431
+ ```
432
+
433
+
434
+ **`before_all`** will run only once, at spec initialization, before any test run:
435
+
436
+ ```ruby
437
+ describe Array do
438
+ before_all { @array = subject.new }
439
+ # @array will be the same for all tests
440
+ end
441
+ ```
442
+
443
+ **Important:** Unlike `before` callbacks, `before_all` ones **are not incremental**, so only the last defined/inherited callback will be called.
444
+
445
+ ### &#8627; `after` and `after_all`
446
+
447
+ `after` will run after each test, regardless was it passed or failed.
448
+
449
+ **Important:** Just like `before` callbacks, `after` ones are incremental, so all callbacks, inherited and defined, will run in appropriate order - first inherited then defined.
450
+
451
+ **`after_all`** will run only once, after all tests finished. It will run regardless tests status.
452
+
453
+ **Important:** Unlike `after` callbacks, `after_all` ones **are not incremental**, so only the last defined/inherited callback will be called.
454
+
455
+ ### &#8627; `around` and `around_all`
456
+
457
+ Allow to run tests inside a predefined wrapper.
458
+
459
+ The block will receive the test as first argument and should call `#run` on it:
460
+
461
+ ```ruby
462
+ describe ActorSystem do
463
+ around do |test|
464
+ Celluloid::ActorSystem.new.within do
465
+ test.run
466
+ end
467
+ end
468
+ # each test will run within own ActorSystem
469
+ end
470
+ ```
471
+
472
+ **Important:** Unlike `before`/`after` callbacks, `around` are not incremental, meant that only the last callback will be called regardless how many callbacks was inherited/defined.
473
+
474
+ `around_all` is similar to `around` except it will run all tests inside given block.
475
+
476
+ It will receive the spec as first argument and should call `run` on it:
477
+
478
+ ```ruby
479
+ require 'tmpdir'
480
+
481
+ describe :FileManager do
482
+ around_all do |spec|
483
+ # running all tests into a temporary folder
484
+ Dir.mktmpdir do
485
+ spec.run
486
+ end
487
+ end
488
+ end
489
+ ```
490
+
491
+
492
+ [&#8679; Table of Contents](#docs)
493
+
494
+ ### &#8627; Filters
495
+
496
+ When you need a callback to run only before/after/around specific test(s), pass that tests names as arguments.
497
+
498
+ *Run only before `:a` and `:b` tests*:
499
+
500
+ ```ruby
501
+ before :a, :b do
502
+ # ...
503
+ end
504
+ ```
505
+
506
+ It is also possible to use `:except` option.
507
+
508
+ *Run after all except `:x`*:
509
+
510
+ ```ruby
511
+ after except: :x do
512
+ # ...
513
+ end
514
+ ```
515
+
516
+ And to make matchers even more useful, test names can be provided as regular expressions.
517
+
518
+ *Run around tests that match `/a/`*:
519
+
520
+ ```ruby
521
+ around /a/ do
522
+ # ...
523
+ end
524
+ ```
525
+
526
+ *Run before tests that match `/a/` but not before `:abc`*:
527
+
528
+ ```ruby
529
+ before /a/, except: :abc do
530
+ # ...
531
+ end
532
+ ```
533
+
534
+
535
+ *Run before tests that match `/a/` but not before ones that match `/ab/`*:
536
+
537
+ ```ruby
538
+ before /a/, except: /ab/ do
539
+ # ...
540
+ end
541
+ ```
542
+
543
+
544
+
545
+ [&#8679; Table of Contents](#docs)
546
+
547
+
548
+ ## Assertions
549
+
550
+ Minispec's assertions mechanism is pretty simple: tested objects are wrapped into a proxy that intercepts messages, sending them to tested object and mark assertion as passed or failed based on returned value.
551
+
552
+ There are plenty of wrappers used in Minispec. `assert` and `expect` are only few of them:
553
+
554
+ ```ruby
555
+ assert(a) == b
556
+ expect(a).include?(b)
557
+ assert(a).nil?
558
+ # etc.
559
+ ```
560
+
561
+ Though these assertions looks mostly ok, they are not truly semantic.<br>
562
+ Let's use some more wrappers:
563
+
564
+ ```ruby
565
+ is(a) == b
566
+ does(a).include?(b)
567
+ is(a).nil?
568
+ # etc.
569
+ ```
570
+
571
+ Here is the list of available wrappers:
572
+
573
+ * `assert`
574
+ * `affirm`
575
+ * `assume`
576
+ * `assure`
577
+ * `expect`
578
+ * `verify`
579
+ * `check`
580
+ * `prove`
581
+ * `would`
582
+ * `will`
583
+ * `is`
584
+ * `is?`
585
+ * `are`
586
+ * `are?`
587
+ * `was`
588
+ * `was?`
589
+ * `does`
590
+ * `does?`
591
+ * `did`
592
+ * `did?`
593
+ * `have`
594
+ * `have?`
595
+ * `has`
596
+ * `has?`
597
+
598
+ [&#8679; Table of Contents](#docs)
599
+
600
+
601
+ ### &#8627; Negative assertions
602
+
603
+ There are two kind of negations in Minispec:
604
+
605
+ * negative wrappers
606
+ * post-wrapper negations
607
+
608
+ List of negative wrappers:
609
+
610
+ * `refute`
611
+ * `negate`
612
+ * `fail_if`
613
+ * `not_expected`
614
+ * `assert_not`
615
+
616
+ ```ruby
617
+ refute(a) == b
618
+ fail_if(a).include?(b)
619
+ # etc.
620
+ ```
621
+
622
+ List of post-wrapper negations:
623
+
624
+ * `not`
625
+ * `has_not`
626
+ * `have_not`
627
+ * `does_not`
628
+ * `did_not`
629
+ * `is_not`
630
+ * `is_not_a`
631
+ * `wont`
632
+
633
+ ```ruby
634
+ assert(a).not == b
635
+ assert(a).does_not.include?(b)
636
+ assert(a).is_not.nil?
637
+ # etc.
638
+ ```
639
+
640
+ [&#8679; Table of Contents](#docs)
641
+
642
+
643
+ ### &#8627; Semantic sugar
644
+
645
+ Just like post-wrapper negations, sugar methods are used after a wrapper and are aimed to add some more semantic sense to assertions.
646
+
647
+ List of semantic sugar methods:
648
+
649
+ * `a`
650
+ * `is`
651
+ * `is_a`
652
+ * `are`
653
+ * `will`
654
+ * `was`
655
+ * `does`
656
+ * `did`
657
+ * `have`
658
+ * `has`
659
+ * `to`
660
+ * `be`
661
+ * `been`
662
+
663
+ ```ruby
664
+ is(x).a.instance_of?(Y)
665
+ assert(a).is.nil?
666
+ expect(x).was.called?
667
+ assert(x).has.been.locked?
668
+ expect(a).to.include?(b)
669
+ expect(a).to.be.empty?
670
+ expect(x).to.have.children
671
+ # etc.
672
+ ```
673
+
674
+ [&#8679; Table of Contents](#docs)
675
+
676
+
677
+ ## Helpers
678
+
679
+ In most cases native Ruby methods are enough for some basic testing.<br>
680
+ However some basic testing is never enough for code that matters.
681
+
682
+ Minispec's helpers system allows to write tests of any complexity without sacrifice simplicity and semantic readability.
683
+
684
+ The idea is simple: if the wrapper detects a helper with same name as received message, it will will pass that message to the helper rather than to the tested object.
685
+
686
+ Helper is receiving tested object as first argument and can apply any assertions on it.
687
+
688
+ It does not mater what a helper returns. If some assertion fails inside a helper, the test that calls the helper will be marked as failed and failure will contain both test and helper's locations.
689
+
690
+ *`blank?` helper not defined, so `blank?` message are passed to `a`. If `a` does not respond to `blank?`, a `NoMethodError` will be raised*:
691
+
692
+ ```ruby
693
+ is(a).blank?
694
+ ```
695
+
696
+ *`blank?` helper defined, so `blank?` message are passed to helper rather than to `a`. `a` may or may not respond to `blank?`*:
697
+
698
+ ```ruby
699
+ # defining a helper
700
+ helper :blank? do |a|
701
+ # validating given object
702
+ is(a.to_s).empty?
703
+ end
704
+
705
+ # defining a test
706
+ should 'return a non-empty string' do
707
+ a = Some.abstract.string
708
+ is(a).blank?
709
+ end
710
+ ```
711
+
712
+ [&#8679; Table of Contents](#docs)
713
+
714
+ ### &#8627; Built-in helpers
715
+
716
+ Minispec comes with some built-in helpers for most common scenarios: raised exceptions, thrown symbols, booleans etc.
717
+
718
+ #### &#8627; `raise` helper
719
+
720
+
721
+ *Without arguments any exception will be accepted*:
722
+
723
+ ```ruby
724
+ does { some risky code }.raise
725
+
726
+ # can also be written as
727
+ does { some risky code }.raise?
728
+ does { some risky code }.raise_error?
729
+ expect { some risky code }.to_raise
730
+ expect { some risky code }.to_raise_error
731
+ ```
732
+
733
+ *When given a class it will accept only exceptions of given class*:
734
+
735
+ ```ruby
736
+ expect { some risky code }.to_raise NoMethodError
737
+ ```
738
+
739
+ *When given a string only exceptions with same message will be accepted*:
740
+
741
+ ```ruby
742
+ expect { some risky code }.to_raise 'some error message'
743
+ ```
744
+
745
+ *When given a Regexp only exceptions with same message as given string will be accepted*:
746
+
747
+ ```ruby
748
+ expect { some risky code }.to_raise /some error message/
749
+ ```
750
+
751
+ *When both class and String/Regexp given, it will accept only exceptions of given class that equals/match given String/Regexp*:
752
+
753
+ ```ruby
754
+ expect { some risky code }.to_raise NoMethodError, 'some error message'
755
+ expect { some risky code }.to_raise NoMethodError, /some error message/
756
+ ```
757
+
758
+ **When you need even more control over raised exception, use a block.**
759
+
760
+ *Expect any error to be raised except LoadError:*
761
+
762
+ ```ruby
763
+ expect { something }.to_raise {|e| e.is_a?(Exception) && e.class != LoadError}
764
+ ```
765
+
766
+ *Expect raised error backtrace to contain a specific line:*
767
+
768
+ ```ruby
769
+ expect { something }.to_raise {|e| e.backtrace.find {|l| l =~ /something/} }
770
+ ```
771
+
772
+ [&#8679; Table of Contents](#docs)
773
+
774
+ #### &#8627; `throw` helper
775
+
776
+ *When called without arguments any thrown symbol accepted*:
777
+
778
+ ```ruby
779
+ does { some code }.throw
780
+
781
+ # can also be written as
782
+ does { some code }.throw?
783
+ does { some code }.throw_symbol?
784
+ expect { some code }.to_throw
785
+ expect { some code }.to_throw_symbol
786
+ ```
787
+
788
+ *When called with a symbol it will pass only if given symbol thrown*:
789
+
790
+ ```ruby
791
+ expect { some code }.to_throw :some_symbol
792
+ ```
793
+
794
+ *When called with a symbol and a value it will pass only if given symbol thrown with given value*:
795
+
796
+ ```ruby
797
+ expect { some code }.to_throw :some_symbol, 'some value'
798
+ ```
799
+
800
+ **Also a block can be used to validate thrown symbol.**<br>
801
+ **Important:** when a block used, only thrown symbol passed to block, so no way to validate the value by block.
802
+
803
+ *Expect any symbol to be thrown except `:halt`*
804
+
805
+ ```ruby
806
+ does { some code }.throw? {|s| s != :halt}
807
+ ```
808
+
809
+ **Limitations:** the code to be inspected for thrown symbols should run out of its `catch` block.
810
+
811
+ *This test wont pass cause thrown symbol are caught early:*
812
+
813
+ ```ruby
814
+ describe User do
815
+
816
+ def create_account *args
817
+ catch :invalid_email do
818
+ User.new *args
819
+ end
820
+ end
821
+
822
+ it 'fails if invalid email given' do
823
+ expect { create_account(email: 'blah') }.to_throw :invalid_email
824
+ end
825
+ end
826
+ ```
827
+
828
+ For this to work you should run `User.new` outside `catch` block.
829
+
830
+ [&#8679; Table of Contents](#docs)
831
+
832
+
833
+ #### &#8627; Boolean helpers
834
+
835
+ *`true?`: expects tested object to be `true`*:
836
+
837
+ ```ruby
838
+ is(a).true?
839
+ assert(a).is.true?
840
+
841
+ # same as
842
+ assert(a) == true
843
+ ```
844
+
845
+ *`false?`: expects tested object to be `false`*:
846
+
847
+ ```ruby
848
+ is(a).false?
849
+ assert(a).is.false?
850
+
851
+ # same as
852
+ assert(a) == false
853
+ ```
854
+
855
+ *`positive`, `positive?`, `truthful?, `non_falsy?`: expects tested object to not be `nil` nor `false`*:
856
+
857
+ ```ruby
858
+ is(a).positive?
859
+ is(a).truthful?
860
+ expect(a).is.positive
861
+ expect(a).is.non_falsy?
862
+ ```
863
+
864
+
865
+ [&#8679; Table of Contents](#docs)
866
+
867
+ #### &#8627; `silent` helper
868
+
869
+ `silent` (aliased as `silent?` and `is_silent`) expects given block to output nothing, that's it, the block should write nothing to STDOUT nor to STDERR.
870
+
871
+ ```ruby
872
+ is { some_code_here }.silent?
873
+ ```
874
+
875
+ or
876
+
877
+ ```ruby
878
+ assert do
879
+ some
880
+ more
881
+ code
882
+ here
883
+ end.is_silent
884
+ ```
885
+
886
+
887
+ [&#8679; Table of Contents](#docs)
888
+
889
+
890
+ #### &#8627; `Array` helpers
891
+
892
+ *`same_elements`: expects tested object is an array that have same elements as given array*:
893
+
894
+ ```ruby
895
+ a = [1, 2, :x]
896
+ b = [:x, 1, 2]
897
+ expect(a).has.same_elements_as(b)
898
+ # => passed
899
+ ```
900
+
901
+ *`contain`: expects tested object is an array that contains given elements. Order does not matter*:
902
+
903
+ ```ruby
904
+ a = [1, 2, :x]
905
+ does(a).contain? :x, 2
906
+ # => passed
907
+ does(a).contain? :y, 2
908
+ # => failed
909
+ ```
910
+
911
+ [&#8679; Table of Contents](#docs)
912
+
913
+
914
+ ### &#8627; Custom helpers
915
+
916
+ The power of Minispec's helpers are revealed in full only when you define your own helpers. It is simply done by using `helper` method with a block. The block will receive tested object as first argument and you can test it to the backbone:
917
+
918
+ ```ruby
919
+ describe Cooking do
920
+ helper :looks_like_a_pizza? do |food|
921
+ assert(food).contains? :olives, :cheese
922
+ end
923
+
924
+ it 'cooks a pizza' do
925
+ food = Cook.new.pizza
926
+ does(food).looks_like_a_pizza?
927
+ end
928
+ end
929
+ ```
930
+
931
+ When you pass some arguments into helper they comes after tested object:
932
+
933
+ ```ruby
934
+ helper :ok_with_body? do |response, body|
935
+ assert(response.status) == 200
936
+ assert(response.body) == body
937
+ end
938
+
939
+ test 'index action' do
940
+ get '/'
941
+ is(last_response).ok_with_body? 'index'
942
+ end
943
+ ```
944
+
945
+ [&#8679; Table of Contents](#docs)
946
+
947
+
948
+ ### &#8627; Helpers with blocks
949
+
950
+ If object passed within a block, the helper will receive that block as first argument.
951
+
952
+ Please note that the block will be received as usual argument rather than a block.
953
+
954
+ ```ruby
955
+ helper :blank? do |block|
956
+ is(block.call).empty?
957
+ # or is(&block).empty?
958
+ end
959
+
960
+ should 'pass' do
961
+ is { '' }.blank?
962
+ end
963
+ ```
964
+
965
+ When a helper used with a block, the block will be passed as last argument,
966
+ in form of a simple argument rather than a block:
967
+
968
+ ```ruby
969
+ helper :any_of? do |arr, block|
970
+ assert(arr).any?(&block)
971
+ end
972
+
973
+ should 'pass' do
974
+ has([1, 2]).any_of? {|v| v > 1}
975
+ end
976
+ ```
977
+
978
+ [&#8679; Table of Contents](#docs)
979
+
980
+
981
+ ### &#8627; Helpers aliases
982
+
983
+ Often you need some helper to be accessed by various names.<br>
984
+ Minispec allows to create helper aliases by using `alias_helper` method.<br>
985
+ Simply pass new name as first argument and existing helper name as second:
986
+
987
+ ```ruby
988
+ helper :open? do |door|
989
+ # ...
990
+ end
991
+ alias_helper :not_closed, :open?
992
+
993
+ it 'creates a open door' do
994
+ door = Door.new(open: true)
995
+ is(door).open?
996
+ end
997
+
998
+ it 'opens door with open!' do
999
+ door = Door.new
1000
+ door.open!
1001
+ assert(door).not_closed
1002
+ end
1003
+ ```
1004
+
1005
+
1006
+ [&#8679; Table of Contents](#docs)
1007
+
1008
+
1009
+ ## Mocking
1010
+
1011
+ Minispec comes with a pretty full set of mocking instruments.
1012
+
1013
+ Though there are obvious differences, these instruments are kind of similar to ones used in another libraries like [mocha](https://github.com/freerange/mocha), [rr](https://github.com/rr/rr) and [rspec-mocks](https://github.com/rspec/rspec-mocks).
1014
+
1015
+ Perhaps sometimes verbose, they gives you full control over mocked objects.
1016
+
1017
+
1018
+ ### &#8627; Expectations
1019
+
1020
+ Useful when you expect some object to receive some message(s).
1021
+
1022
+ Expectations are validated after current test evaluation finished. So the object are expected to receive given message(s) somewhere in the near future, just before current test ends.
1023
+
1024
+ Use `to_receive` helper to add an expectation.
1025
+
1026
+ *Expect bob to eat an apple:*
1027
+
1028
+ ```ruby
1029
+ apple = Apple.new
1030
+ bob = Kid.new
1031
+ bob.bag << apple
1032
+ expect(apple).to_receive(:eaten)
1033
+ ```
1034
+
1035
+ [&#8679; Table of Contents](#docs)
1036
+
1037
+
1038
+ **&#8627; Expecting multiple messages**
1039
+
1040
+ Often you need to expect multiple messages on a object.
1041
+
1042
+ You could of course add a expectation for each message:
1043
+
1044
+ ```ruby
1045
+ expect(a).to_receive :x
1046
+ expect(a).to_receive :y
1047
+ ```
1048
+
1049
+ but this is tedious and becomes hairy very quickly.
1050
+
1051
+ Recommended approach is to use `:to_receive` helper with multiple arguments:
1052
+
1053
+ ```ruby
1054
+ expect(a).to_receive(:x, :y)
1055
+ ```
1056
+
1057
+ Much better, huh?
1058
+
1059
+ **&#8627; Assert given message(s) never received**
1060
+
1061
+ *Ensure `a` wont receive `:b` message:*
1062
+
1063
+ ```ruby
1064
+ expect(a).to_not.receive(:b)
1065
+ ```
1066
+
1067
+ *same:*
1068
+
1069
+ ```ruby
1070
+ refute(a).receive(:b)
1071
+ ```
1072
+
1073
+ *Ensure `a` wont receive `:x` message nor `:y`:*
1074
+
1075
+ ```ruby
1076
+ assert(a).wont.receive(:x, :y)
1077
+ ```
1078
+
1079
+ if at least one of messages received, the test will fail.
1080
+
1081
+ [&#8679; Table of Contents](#docs)
1082
+
1083
+
1084
+ ### &#8627; Constraints
1085
+
1086
+ Sometimes just checking that some messages are received is not enough. We need to know whether certain message received with certain arguments and returned/raised/thrown certain value.
1087
+
1088
+ Minispec allows to add such kind of constraints with ease.
1089
+
1090
+
1091
+ #### &#8627; Arguments
1092
+
1093
+ *Expect `a` to receive `:b` message with x, y arguments:*
1094
+
1095
+ ```ruby
1096
+ expect(a).to_receive(:b).with('x', 'y')
1097
+ ```
1098
+
1099
+ *Expect `a` to receive `:b` message with whatever arguments:*
1100
+
1101
+ ```ruby
1102
+ expect(a).to_receive(:b).with {|*| true}
1103
+ ```
1104
+
1105
+ *Expect `a` to receive `:b` message with exactly 2 arguments, whatever they are:*
1106
+
1107
+ ```ruby
1108
+ expect(a).to_receive(:b).with {|*a| a.size == 2}
1109
+ ```
1110
+
1111
+ *Expect at least 2 arguments and second one to be bigger than first:*
1112
+
1113
+ ```ruby
1114
+ expect(a).to_receive(:b).with {|x,y| y > x}
1115
+ ```
1116
+
1117
+ *Expect exactly 2 arguments and second one to be bigger than first:*
1118
+
1119
+ ```ruby
1120
+ expect(a).to_receive(:b).with do |*a|
1121
+ assert(a.size) == 2
1122
+ is(a.last) > a.first
1123
+ end
1124
+ ```
1125
+
1126
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1127
+
1128
+ ```ruby
1129
+ expect(a).to_receive(:b).with {|*a| a.size == 2 && a.last > a.first}
1130
+ ```
1131
+
1132
+
1133
+ **&#8627; Arguments on multiple expectations**
1134
+
1135
+ *Expect `a` to receive `:x` and `:y` messages with 1 and 2 arguments respectively:*
1136
+
1137
+ ```ruby
1138
+ expect(a).to_receive(:x, :y).with(1, 2)
1139
+ ```
1140
+
1141
+ for this test to pass, both `a.x(1)` and `a.y(2)` should be called.
1142
+
1143
+ You can also use a block to validate arguments.
1144
+
1145
+ *Expect `a` to receive `:x` and `:y` messages where x's argument is 1 and y's argument is bigger than 2:*
1146
+
1147
+ ```ruby
1148
+ expect(a).to_receive(:x, :y).with do |x,y|
1149
+ is(x) == 1
1150
+ is(y) > 2
1151
+ end
1152
+ ```
1153
+
1154
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1155
+
1156
+ ```ruby
1157
+ expect(a).to_receive(:x, :y).with {|x,y| x == 1 && y > 2}
1158
+ ```
1159
+
1160
+ [&#8679; Table of Contents](#docs)
1161
+
1162
+
1163
+ #### &#8627; Returned value
1164
+
1165
+ *Expect `a` to receive `:b` message and return 'x':*
1166
+
1167
+ ```ruby
1168
+ expect(a).to_receive(:b).and_return('x')
1169
+ ```
1170
+
1171
+ *Expect `a` to receive `:b` message and return a value bigger than 10:*
1172
+
1173
+ ```ruby
1174
+ expect(a).to_receive(:b).and_return {|returned_value| is(returned_value) > 10}
1175
+ ```
1176
+
1177
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1178
+
1179
+ ```ruby
1180
+ expect(a).to_receive(:b).and_return {|returned_value| returned_value > 10}
1181
+ ```
1182
+
1183
+
1184
+ **&#8627; Returned value on multiple expectations**
1185
+
1186
+ *Expect `a` to receive `:x` and `:y` messages and return 1 and 2 respectively:*
1187
+
1188
+ ```ruby
1189
+ expect(a).to_receive(:x, :y).and_return(1, 2)
1190
+ ```
1191
+
1192
+ for this test to pass, `a.x` should return 1 and `a.y` should return 2.
1193
+
1194
+ If all messages expected to return same value, use a single argument.
1195
+
1196
+ *Expect `a` to receive `:x` and `:y` messages and both to return 1:*
1197
+
1198
+ ```ruby
1199
+ expect(a).to_receive(:x, :y).and_return(1)
1200
+ ```
1201
+
1202
+ for this to pass both `:x` and `:y` should return 1.
1203
+
1204
+ When you need full control over returned values, use a block.
1205
+
1206
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will return 1 and `:y` a value bigger than 5:*
1207
+
1208
+ ```ruby
1209
+ expect(a).to_receive(:x, :y).and_return do |x,y|
1210
+ is(x) == 1
1211
+ is(y) > 5
1212
+ end
1213
+ ```
1214
+
1215
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1216
+
1217
+ ```ruby
1218
+ expect(a).to_receive(:x, :y).and_return {|x,y| x == 1 && y > 5}
1219
+ ```
1220
+
1221
+ [&#8679; Table of Contents](#docs)
1222
+
1223
+
1224
+ #### &#8627; Raised exception
1225
+
1226
+ When you expect a message to raise a exception, use `and_raise` expectation.
1227
+
1228
+ *Expect `a` to receive `:b` message and raise something:*
1229
+
1230
+ ```ruby
1231
+ expect(a).to_receive(:b).and_raise
1232
+ ```
1233
+
1234
+ When you expect a specific error, pass expected error class as first argument.
1235
+
1236
+ *Expect `a` to receive `:b` message and raise NoMethodError error:*
1237
+
1238
+ ```ruby
1239
+ expect(a).to_receive(:b).and_raise NoMethodError
1240
+ ```
1241
+
1242
+
1243
+ When you expect a specific error with a specific message, pass expected error class and expected message wrapped into an array.
1244
+
1245
+ *Expect `a` to receive `:b` message and raise CustomError error with 'something went wrong' message:*
1246
+
1247
+ ```ruby
1248
+ expect(a).to_receive(:b).and_raise [CustomError, 'something went wrong']
1249
+ ```
1250
+
1251
+
1252
+ When you need error message to match some string, use a Regexp.
1253
+
1254
+ *Expect `a` to receive `:b` message and raise CustomError error with a message that match `/something/`:*
1255
+
1256
+ ```ruby
1257
+ expect(a).to_receive(:b).and_raise [CustomError, /something/]
1258
+ ```
1259
+
1260
+ **When you need even more control over raised exception, use a block.**
1261
+
1262
+ *Expect `a` to receive `:b` message and raise anything but LoadError:*
1263
+
1264
+ ```ruby
1265
+ expect(a).to_receive(:b).and_raise do |e|
1266
+ assert(e).is_a?(Exception)
1267
+ assert(e.class) != LoadError
1268
+ end
1269
+ ```
1270
+
1271
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1272
+
1273
+ ```ruby
1274
+ expect(a).to_receive(:b).and_raise do |e|
1275
+ e.is_a?(Exception) && e.class != LoadError
1276
+ end
1277
+ ```
1278
+
1279
+
1280
+ **&#8627; Raised exception on multiple expectations**
1281
+
1282
+ *Expect `a` to receive `:x` and `:y` messages and both to raise something:*
1283
+
1284
+ ```ruby
1285
+ expect(a).to_receive(:x, :y).and_raise
1286
+ ```
1287
+
1288
+ If you expect a specific error for each message to be raised, just pass expected errors as arguments.
1289
+
1290
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will raise NoMethodError error and `:y` will raise StandardError:*
1291
+
1292
+ ```ruby
1293
+ expect(a).to_receive(:x, :y).and_raise(NoMethodError, StandardError)
1294
+ ```
1295
+
1296
+ If you need also to check error messages, pass error class and message wrapped into an array.
1297
+
1298
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will raise NoMethodError that match /X/ and `:y` will raise StandardError that match /Y/:*
1299
+
1300
+ ```ruby
1301
+ expect(a).to_receive(:x, :y).and_raise([NoMethodError, /X/], [StandardError, /Y/])
1302
+ ```
1303
+
1304
+ It's not a sin to expect only error type on some message and error type with message on another.
1305
+
1306
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will raise NoMethodError and `:y` will raise StandardError that match /Y/:*
1307
+
1308
+ ```ruby
1309
+ expect(a).to_receive(:x, :y).and_raise(NoMethodError, [StandardError, /Y/])
1310
+ ```
1311
+
1312
+ **If all messages are expected to raise same error, use a single argument.**
1313
+
1314
+ *Expect `a` to receive `:x` and `:y` messages and both to raise StandardError:*
1315
+
1316
+ ```ruby
1317
+ expect(a).to_receive(:x, :y).and_raise(StandardError)
1318
+ ```
1319
+
1320
+ **It is also possible to use a block for validating raised exceptions.**<br>
1321
+ The block will receive as many arguments as messages expected. Each argument will be a exception instance if its message raised something or `nil` otherwise.
1322
+
1323
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will raise NoMethodError and `:y`'s backtrace will contain a specific line:*
1324
+
1325
+ ```ruby
1326
+ expect(a).to_receive(:x, :y).and_raise do |x,y|
1327
+ assert(x).is_a? NoMethodError
1328
+ assert(y.backtrace).any? {|l| l =~ /something/}
1329
+ end
1330
+ ```
1331
+
1332
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1333
+
1334
+ ```ruby
1335
+ expect(a).to_receive(:x, :y).and_raise do |x,y|
1336
+ x.is_a?(NoMethodError) && y.backtrace.any? {|l| l =~ /something/}
1337
+ end
1338
+ ```
1339
+
1340
+
1341
+ **&#8627; Assert nothing raised**
1342
+
1343
+ Often you need to assure some message received and nothing raised. There are `without_raise` expectation that will ensure nothing raised on message receiving.
1344
+
1345
+ *Expect `a` to receive `:b` and nothing raises:*
1346
+
1347
+ ```ruby
1348
+ expect(a).to_receive(:b).without_raise
1349
+ ```
1350
+
1351
+ Also works on multiple expectations. In this case the test will fail if at least one message raises a exception.
1352
+
1353
+ *Expect `a` to receive `:x` and `:y` without raise anything:*
1354
+
1355
+ ```ruby
1356
+ expect(a).to_receive(:x, :y).without_raise
1357
+ ```
1358
+
1359
+
1360
+ [&#8679; Table of Contents](#docs)
1361
+
1362
+
1363
+ #### &#8627; Thrown symbol
1364
+
1365
+ When you expect a symbol to be thrown, use `and_throw` expectation.
1366
+
1367
+ *Expect `a` to receive `:b` message and throw `:x` symbol:*
1368
+
1369
+ ```ruby
1370
+ expect(a).to_receive(:b).and_throw :x
1371
+ ```
1372
+
1373
+ **Note:** unlike `and_raise` expectation, `and_throw` can not be used without arguments. It requires exactly one argument - the expected symbol(unless a block used).
1374
+
1375
+ Also a block can be used to validate thrown symbol. This is the case when `and_throw` expectation should be used without arguments.
1376
+
1377
+ *Expect `a` to receive `:b` message and throw any symbol except `:x`:*
1378
+
1379
+ ```ruby
1380
+ expect(a).to_receive(:b).and_throw do |s|
1381
+ assert(s).is_a? Symbol # making sure something actually thrown
1382
+ assert(s) != :x
1383
+ end
1384
+ ```
1385
+
1386
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1387
+
1388
+ ```ruby
1389
+ expect(a).to_receive(:b).and_throw {|s| s.is_a?(Symbol) && s != :x }
1390
+ ```
1391
+
1392
+
1393
+ **Limitations:** unlike `throw?` helper, expectations can only check for thrown symbol, so there is no way to get and validate thrown value with expectations.
1394
+
1395
+
1396
+ **&#8627; Thrown symbol on multiple expectations**
1397
+
1398
+ When multiple messages expected, `and_throw` method will accept same number of arguments as the number of expected messages.
1399
+
1400
+ *Expect `a` to receive `:x` and `:y` messages and throw `:xs` and `:ys` symbols respectively:*
1401
+
1402
+ ```ruby
1403
+ expect(a).to_receive(:x, :y).and_throw(:sx, :sy)
1404
+ ```
1405
+
1406
+ If all messages are expected to raise same symbol, use a single argument.
1407
+
1408
+ *Expect `a` to receive `:x` and `:y` messages and both to throw `:halt` symbol:*
1409
+
1410
+ ```ruby
1411
+ expect(a).to_receive(:x, :y).and_throw(:halt)
1412
+ ```
1413
+
1414
+ When you need even more control over thrown symbols, use a block. The block will receive exactly same number of arguments as the number of expected messages. Each argument will be a symbol if its message thrown something of `nil` otherwise.
1415
+
1416
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will thrown `:ok` symbol and `:y` will throw anything but `:halt` symbol:*
1417
+
1418
+ ```ruby
1419
+ expect(a).to_receive(:x, :y).and_throw do |x,y|
1420
+ is(x) == :ok
1421
+ assert(y).is_a? Symbol # making sure something actually thrown
1422
+ assert(y) != :halt
1423
+ end
1424
+ ```
1425
+
1426
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1427
+
1428
+ ```ruby
1429
+ expect(a).to_receive(:x, :y).and_throw do |x,y|
1430
+ x == :ok && y.is_a?(Symbol) && y != :halt
1431
+ end
1432
+ ```
1433
+
1434
+ **&#8627; Assert nothing thrown**
1435
+
1436
+ *Expect `a` to receive `:b` message without throw any symbol:*
1437
+
1438
+ ```ruby
1439
+ expect(a).to_receive(:b).without_throw
1440
+ ```
1441
+
1442
+ *Expect `a` to receive `:x` and `:y` messages without throw any symbol:*
1443
+
1444
+ ```ruby
1445
+ expect(a).to_receive(:x, :y).without_throw
1446
+ ```
1447
+
1448
+
1449
+ [&#8679; Table of Contents](#docs)
1450
+
1451
+
1452
+ #### &#8627; Yielded arguments
1453
+
1454
+ `and_yield` expectation allow to check whether some block inside expected message yielded with specific arguments.
1455
+
1456
+ *Expect `a` to receive `:b` message and `:b` message to yield a block with 1, 2 arguments:*
1457
+
1458
+ ```ruby
1459
+ expect(a).to_receive(:b).and_yield(1, 2)
1460
+ ```
1461
+
1462
+ When you need more control, use a block.
1463
+
1464
+ *Expect `a` to receive `:b` message and `:b` message to yield a block where first argument is a string and last is a symbol:*
1465
+
1466
+ ```ruby
1467
+ expect(a).to_receive(:b).and_yield do |*args|
1468
+ assert(a.first).is_a? String
1469
+ assert(a.last).is_a? Symbol
1470
+ end
1471
+ ```
1472
+
1473
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1474
+
1475
+ ```ruby
1476
+ expect(a).to_receive(:b).and_yield do |*args|
1477
+ a.first.is_a?(String) && a.last.is_a?(Symbol)
1478
+ end
1479
+ ```
1480
+
1481
+
1482
+ **&#8627; Yielded arguments on multiple expectations**
1483
+
1484
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will yield a block with 1, 2 arguments and `:y` will yield a bloc with `:z` argument:*
1485
+
1486
+ ```ruby
1487
+ expect(a).to_receive(:x, :y).and_yield([1, 2], :z)
1488
+ ```
1489
+
1490
+ If all messages are expected to yield a block with same arguments, use a single argument on `and_yield` method.
1491
+
1492
+
1493
+ *Expect `a` to receive `:x` and `:y` messages and both `:x` to yield a block with `:z` argument:*
1494
+
1495
+ ```ruby
1496
+ expect(a).to_receive(:x, :y).and_yield(:z)
1497
+ ```
1498
+
1499
+
1500
+ *Expect `a` to receive `:x` and `:y` messages and both to yield a block with 1, 2 arguments:*
1501
+
1502
+ ```ruby
1503
+ expect(a).to_receive(:x, :y).and_yield([1, 2])
1504
+ ```
1505
+
1506
+ Please note the arguments are wrapped into an array. If using `and_yield(1, 2)` instead, the test will expect `:x` to yield 1 and `:y` to yield 2.
1507
+
1508
+ When you need full control over yielded arguments, use a block.
1509
+
1510
+ *Expect `a` to receive `:x` and `:y` messages where `:x` will yield a block with 2 or more arguments and `:y` will yield a bloc with integer only arguments:*
1511
+
1512
+ ```ruby
1513
+ expect(a).to_receive(:x, :y).and_yield do |*args|
1514
+ assert(args.first.size) >= 2
1515
+ assert(args.last).all? {|a| a.is_a? Integer}
1516
+ end
1517
+ ```
1518
+
1519
+ Another way is to just return `true` or `false`. If block returns true, the test will pass.
1520
+
1521
+ ```ruby
1522
+ expect(a).to_receive(:x, :y).and_yield do |*args|
1523
+ args.first.size >= 2 && args.last.all? {|a| a.is_a? Integer}
1524
+ end
1525
+ ```
1526
+
1527
+
1528
+ **&#8627; Assert nothing yielded**
1529
+
1530
+ *Expect `a` to receive `:b` message without yield any block:*
1531
+
1532
+ ```ruby
1533
+ expect(a).to_receive(:b).without_yield
1534
+ ```
1535
+
1536
+ *Expect `a` to receive `:x` and `:y` messages without yield any block:*
1537
+
1538
+ ```ruby
1539
+ expect(a).to_receive(:x, :y).without_yield
1540
+ ```
1541
+
1542
+
1543
+ [&#8679; Table of Contents](#docs)
1544
+
1545
+
1546
+ #### &#8627; Messages Amount
1547
+
1548
+ `count`, or its alias `times`, allow to check how many times a specific message was received.
1549
+
1550
+ *Expect `a` to receive `:b` message exactly 2 times:*
1551
+
1552
+ ```ruby
1553
+ expect(a).to_receive(:b).count(2)
1554
+ ```
1555
+
1556
+ *Expect `a` to receive `:b` message 2 or more times:*
1557
+
1558
+ ```ruby
1559
+ expect(a).to_receive(:b).count {|n| n >= 2}
1560
+ ```
1561
+
1562
+
1563
+ **&#8627; Amount on multiple expectations**
1564
+
1565
+ When multiple messages expected, `count` will receive an argument per each message.
1566
+
1567
+ *Expect `a` to receive `:x` message 2 times and `:y` message 5 times:*
1568
+
1569
+ ```ruby
1570
+ expect(a).to_receive(:x, :y).count(2, 5)
1571
+ ```
1572
+
1573
+ When all messages are expected to receive same amount of times, use a single argument.
1574
+
1575
+ *Expect `a` to receive `:x` and `:y` messages exactly 2 times each:*
1576
+
1577
+ ```ruby
1578
+ expect(a).to_receive(:x, :y).count(2)
1579
+ ```
1580
+
1581
+ *Expect `a` to receive `:x` message exactly 2 times and `:y` message at least once:*
1582
+
1583
+ ```ruby
1584
+ expect(a).to_receive(:x, :y).count {|x,y| x == 2 && y > 1}
1585
+ ```
1586
+
1587
+
1588
+ [&#8679; Table of Contents](#docs)
1589
+
1590
+ #### &#8627; Messages Order
1591
+
1592
+ Unlike RSpec, ordering in Minispec works only with multiple messages.
1593
+
1594
+ *Expect `a` to receive `:x`, `:y`, `:z` messages exactly in specified order:*
1595
+
1596
+ ```ruby
1597
+ expect(a).to_receive(:x, :y, :z).ordered
1598
+ ```
1599
+
1600
+ If given messages will be received in another order, the test will fail.
1601
+
1602
+ It is also possible to check whether same sequence of messages received N times.
1603
+
1604
+ *Expect `a` to receive `:x`, `:y` sequence exactly 2 times:*
1605
+
1606
+ ```ruby
1607
+ expect(a).to_receive(:x, :y).ordered(2)
1608
+ ```
1609
+
1610
+ for this test to pass following code expected to be executed:
1611
+
1612
+ ```ruby
1613
+ a.x
1614
+ a.y
1615
+ a.x
1616
+ a.y
1617
+ ```
1618
+
1619
+ if at least one message not received or received in wrong order, the test will fail.
1620
+
1621
+ When you need more flexibility on received sequence, use a block.
1622
+
1623
+ *Expect `a` to receive `:x`, `:y` sequence at least once:*
1624
+
1625
+ ```ruby
1626
+ expect(a).to_receive(:x, :y).ordered {|n| n >= 1}
1627
+ ```
1628
+
1629
+ [&#8679; Table of Contents](#docs)
1630
+
1631
+
1632
+ ### Spies
1633
+
1634
+ Just like expectations, spies checks for some object to receive specific message(s). The only logical difference is that spies assumes message(s) was already received rather than expects they to be received in the future.
1635
+
1636
+ Also there is a technical difference - while expectations does not require any preparations on the objects, spies does. You should explicitly "attach a spy" on the object and specify what methods to spy on, let the object to behave in its way and only after that you can check whether it received expected messages.
1637
+
1638
+ Attaching a spy on a object is easily done via `spy` method.<br>
1639
+ Checking a message was received is done via `received` helper(or its sugar alias `received?`).
1640
+
1641
+ ```ruby
1642
+ user = User.new
1643
+ spy(user, :location) # attaching spy...
1644
+ user.summary
1645
+ assert(user).received(:location) # checking location message received
1646
+ ```
1647
+
1648
+ In terms of what happens after message received spies behaves exactly as expectations:
1649
+
1650
+ * checks arguments message(s) was received with
1651
+ * `with`
1652
+ * validates returned value(s)
1653
+ * `and_returned`
1654
+ * checks for raised errors
1655
+ * `and_raised`
1656
+ * checks for thrown symbols
1657
+ * `and_thrown`
1658
+ * validates yielded arguments
1659
+ * `and_yielded`
1660
+ * checks how many times message(s) was received
1661
+ * `count`
1662
+ * checks messages was received in specific order
1663
+ * `ordered`
1664
+
1665
+
1666
+ And just as with expectations, spies behaves well when dealing with multiple messages. Just attach a spy on multiple messages and validate them all at once:
1667
+
1668
+ ```
1669
+ spy(user, :name, :age, :location)
1670
+ user.summary
1671
+ assert(user).received(:name, :age, :location)
1672
+ ```
1673
+
1674
+
1675
+ [&#8679; Table of Contents](#docs)
1676
+
1677
+
1678
+ ### Stubs
1679
+
1680
+ Minispec allows to stub any method on a given object and have full control over stub behavior.
1681
+
1682
+ *Add `:x` stub:*
1683
+
1684
+ ```ruby
1685
+ stub(some_object, :x)
1686
+ ```
1687
+
1688
+ `some_object.x` will return `nil`.
1689
+
1690
+ When you need a stub to return some value, regardless given arguments, use a Hash or a block.
1691
+
1692
+ *Add `:x` stub and make it return `:y`:*
1693
+
1694
+ ```ruby
1695
+ stub(some_object, :x => :y)
1696
+ ```
1697
+
1698
+ `some_object.x` will return `:y`.
1699
+
1700
+ *Add `:x` stub and make it return `:z`:*
1701
+
1702
+ ```ruby
1703
+ stub(some_object, :x) { :z }
1704
+ ```
1705
+
1706
+ now `some_object.x` will return `:z`.
1707
+
1708
+ **Important!** Stubs does not impose any restrictions on arity, so stubbed methods can be called with any arguments!
1709
+
1710
+ Given arguments will just be passed into the block, preceded by the original. That's it, the block will receive the original method as first argument. If stubbed method were not defined on that object before stubbing, the block will receive `nil` as first argument.
1711
+
1712
+ Another important note is that method's visibility are kept even after they are stubbed. So if some method exists on target object and it is protected, the stub that will override original method will be protected as well.<br>
1713
+ Same for private and public methods.
1714
+
1715
+ And of course if we are stubbing some object that will still exists after test finished, the stubbed methods will be restored to their originals.
1716
+
1717
+
1718
+ [&#8679; Table of Contents](#docs)
1719
+
1720
+
1721
+ #### &#8627; Argument-vary stubs
1722
+
1723
+ Often you need a stub to behave in a way when receiving some arguments and another way when receiving another arguments.
1724
+
1725
+ At a first glance this could be done by comparing arguments inside the block:
1726
+
1727
+ *Bad!*
1728
+
1729
+ ```ruby
1730
+ stub(some_object, :some_method) do |orig, *args|
1731
+ if args == [:a, :b]
1732
+ # do this
1733
+ elsif args == [:x, :y]
1734
+ # do that
1735
+ end
1736
+ end
1737
+ ```
1738
+
1739
+ however this approach is tedious(at least) and really ugly.
1740
+
1741
+ Recommended way here is to use a block with each sequence of arguments.
1742
+
1743
+ For this to work you'd need to use `with` method.<br>
1744
+ It takes expected arguments and a block to be yielded when the stub called with given arguments:
1745
+
1746
+ ```ruby
1747
+ stub(some_object, :some_method).
1748
+ with(:a, :b) { 'called with a, b' }.
1749
+ with(:x, :y) { 'called with x, y' }
1750
+ ```
1751
+
1752
+ now `some_object.some_method(:a, :b)` will return 'called with a, b' and `some_object.some_method(:x, :y)` will return 'called with x, y'.
1753
+
1754
+ **However!** if called without arguments or with any arguments except [:a, :b] and [:x, :y], this example will actually return `nil`.
1755
+
1756
+ To define a "catchall" add one more block using `with_any`(or simply `any`):
1757
+
1758
+ ```ruby
1759
+ stub(some_object, :some_method).
1760
+ with(:a, :b) { 'called with a, b' }.
1761
+ with(:x, :y) { 'called with x, y' }.
1762
+ with_any { 'whatever' }
1763
+ ```
1764
+
1765
+ now when calling `some_method` **without arguments** or with **any arguments but** [:a, :b] and [:x, :y], it will return 'whatever'.
1766
+
1767
+ `with_any` can also be used with a value rather than a block. Also it can be placed anywhere in the chain, the order does not change the result:
1768
+
1769
+ ```ruby
1770
+ stub(some_object, :some_method).
1771
+ with_any('whatever').
1772
+ with(:a, :b) { 'called with a, b' }.
1773
+ with(:x, :y) { 'called with x, y' }
1774
+ ```
1775
+
1776
+
1777
+ **One more note:** if you prefer a more verbose style you can use `stub` method multiple times:
1778
+
1779
+ ```ruby
1780
+ stub(some_object, :some_method).with(:a, :b) { 'called with a, b' }
1781
+ stub(some_object, :some_method).with(:x, :y) { 'called with x, y' }
1782
+ stub(some_object, :some_method).any { 'whatever' } # or `any('whatever')`
1783
+ ```
1784
+
1785
+ this will work exactly the same way as chained syntax.
1786
+
1787
+ [&#8679; Table of Contents](#docs)
1788
+
1789
+
1790
+ #### &#8627; Stubbing multiple methods at once
1791
+
1792
+ Often you need to stub multiple methods and you feel that calling `stub` for each one is at least tedious.
1793
+
1794
+ And you will be right. Cause Minispec allows to add multiple stubs in one call. For this to work simply use `stubs` instead of `stub`.
1795
+
1796
+ *Stub `:x`, `:y` and `:z` methods on `cube`:*
1797
+
1798
+ ```ruby
1799
+ stubs(cube, :x, :y, :z)
1800
+ ```
1801
+
1802
+ **Worth to note** that given block will apply to all stubs.
1803
+
1804
+ *Stub `:x`, `:y` and `:z` methods on `cube` and make them all return the square of given value:*
1805
+
1806
+ ```ruby
1807
+ stubs(cube, :x, :y, :z) {|orig, n| n ** 2}
1808
+ ```
1809
+
1810
+ now `cube.x(2)` will return 4, `cube.y(4)` will return 16 etc.
1811
+
1812
+ Same for `with` and `with_any` methods - they apply to all stubbed methods without a way to different constraints for some stub:
1813
+
1814
+ ```ruby
1815
+ stubs(a, :b, :c).
1816
+ with(1) {:one}.
1817
+ with(2) {:two}.
1818
+ with_any {:whatever}
1819
+ ```
1820
+
1821
+ now both `a.b(1)` and `a.c(1)` will return :one, both `a.b(2)` and `a.c(2)` will return :two and any of `a.b`/`a.c` without arguments or with any arguments but 1 or 2 will return :whatever.
1822
+
1823
+ Also the method's visibility will be kept, so if some protected exists on target object, the stub will be protected as well. Same for private and public methods.
1824
+
1825
+ And of course multiple protected/private stubs can be defined by using `protected_stubs` and `private_stubs` accordingly.
1826
+
1827
+
1828
+ [&#8679; Table of Contents](#docs)
1829
+
1830
+
1831
+ #### &#8627; Chained stubs
1832
+
1833
+ When you need to stub a chain of methods in one statement use a string of dot separated methods:
1834
+
1835
+ ```ruby
1836
+ stub(a, 'x.y.z')
1837
+ ```
1838
+
1839
+ now `a.x.y.z` will work, though it will return `nil`.
1840
+
1841
+ When you need last method in the chain to return some value, use Hash or a block:
1842
+
1843
+ ```ruby
1844
+ stub(a, 'b.c' => :z)
1845
+ ```
1846
+
1847
+ now `a.b.c` will return `:z`.
1848
+
1849
+ ```ruby
1850
+ stub(a, 'b.c') { :x }
1851
+ ```
1852
+
1853
+ now `a.b.c` will return `:x`.
1854
+
1855
+ A important difference from regular stubs is that chained ones wont receive the original as first argument. They will only receive the arguments passed when stub called:
1856
+
1857
+ ```ruby
1858
+ stub(a, 'b.c') {|x| x ** 2}
1859
+ a.b.c(4)
1860
+ # => 16
1861
+ ```
1862
+
1863
+ If a block given when calling last method in the chain, it will be passed into the block alongside with any arguments. However you'll can not use `yield` here. You should receive it as argument and call it explicitly:
1864
+
1865
+ ```ruby
1866
+ stub(a, 'b.c') do |n, block|
1867
+ block.call(n)
1868
+ end
1869
+
1870
+ a.b.c(4) {|y| y ** 2}
1871
+ # => 16
1872
+ ```
1873
+
1874
+
1875
+ **Important!** just like regular stubs, chained ones may have arguments-vary behavior:
1876
+
1877
+ ```ruby
1878
+ stub(a, 'b.c').
1879
+ with(1) {:one}.
1880
+ with(2) {:two}.
1881
+ with_any { :whatever }
1882
+ ```
1883
+
1884
+ now `a.b.c(1)` will return :one and `a.b.c(2)` will return :two. If called without arguments or with any but 1 or 2, it will return :whatever.
1885
+
1886
+ **Worth to note** that chained stubs does not care about method visibility. It will always define a public singleton method on the target object:
1887
+
1888
+ So, `stub(a, 'b.c')` will define `b` public singleton method on `a`, even if `b` exists and it is protected/private.<br>
1889
+ That's the big difference from regular stubs where stubs keeps same visibility as original methods.
1890
+
1891
+ Please be aware that if the method to be stubbed already exists on the target object, it will be overridden for the time of test running and restored after the test finished.
1892
+
1893
+
1894
+ [&#8679; Table of Contents](#docs)
1895
+
1896
+
1897
+ #### &#8627; Calling original
1898
+
1899
+ If stubbed method already exists, the original method will be passed into block as first argument. Otherwise the block will receive `nil` as first argument.
1900
+
1901
+ ```ruby
1902
+ stub(API, :request) do |original, *args, &block|
1903
+ # call the original with given args and block
1904
+ original.call(*args, &block)
1905
+ end
1906
+ ```
1907
+
1908
+ `API.request` will call our stub which will then call the original.
1909
+
1910
+
1911
+ [&#8679; Table of Contents](#docs)
1912
+
1913
+
1914
+ #### &#8627; Stubs visibility
1915
+
1916
+ Keeping the SUT(system under test) in nearly same state as it would act in a real environment is a high priority matter for Minispec.
1917
+
1918
+ That's why when it is stubbing methods it is keeping original method visibility. Meant if a method were protected before stubbing, the stub will be protected as well. Same for private and public methods.
1919
+
1920
+ However if you want to enforce specific visibility on stubbed method, use one of `public_stub`, `protected_stub` or `private_stub`.<br>
1921
+ They will define a stub with a specific visibility regardless the visibility of original method.
1922
+
1923
+ And of course there are their counterparts for multiple stubbing: `public_stubs`, `protected_stubs` or `private_stubs`.
1924
+
1925
+
1926
+ [&#8679; Table of Contents](#docs)
1927
+
1928
+
1929
+ ### Mocks
1930
+
1931
+ Basically a mock is a mix of a stub and a expectation. Meant that you do not need to separately stub a method then add an expectation on it. Mocks doing this automatically.
1932
+
1933
+ *Stub method `:x` and ensure it will be called by the end of test:*
1934
+
1935
+ ```ruby
1936
+ mock(some_object, :x)
1937
+ ```
1938
+
1939
+ that's it.
1940
+
1941
+ This is a replacement for:
1942
+
1943
+ ```ruby
1944
+ stub(some_object, :x)
1945
+ expect(some_object).to_receive(:x)
1946
+ ```
1947
+
1948
+ **Worth to note** that expectations added by mocks are very basic ones, they will only expect message to be received. That's it, no arguments constraints, no returned value validation etc. If you need a more complex expectation you'll have to define it explicitly.
1949
+
1950
+ **Another important note:** `mock` method will actually return a stub, so you have full control over stubbed method's behavior.
1951
+
1952
+ *Mock method `:x` by making it return `:one` when called with 1:*
1953
+
1954
+ ```ruby
1955
+ mock(some_object, :x).with(1) { :one }
1956
+ ```
1957
+
1958
+ *Mock method `:x` by making it return `:one` when called with 1 and return 'whatever' when called with any other arguments or without arguments at all:*
1959
+
1960
+ ```ruby
1961
+ mock(some_object, :x).
1962
+ with(1) { :one }.
1963
+ with_any { 'whatever' }
1964
+ ```
1965
+
1966
+
1967
+ **Mocks also works with Hashes.**
1968
+
1969
+ *Mock method `:a` to return `:x` and method `:b` to return `:y`:*
1970
+
1971
+ ```ruby
1972
+ mock(some_object, :a => :x, :b => :x)
1973
+ ```
1974
+
1975
+ And as with stubs you can not use arguments filters when mocked methods given as a Hash.<br>
1976
+ This will raise an ArgumentError: `mock(some_object, :a => :x).with(...) {...}`.<br>
1977
+ Same for `with_any`.
1978
+
1979
+
1980
+ There is also a way to **mock multiple methods at once.**
1981
+
1982
+ For this simply use `mocks` instead of `mock`.
1983
+
1984
+ *Mock `:a` and `:b` methods:*
1985
+
1986
+ ```ruby
1987
+ mock(some_object, :a, :b)
1988
+ ```
1989
+
1990
+ for this to pass both `some_object.a` and `some_object.b` should be called.
1991
+
1992
+ When mocking multiple methods, the returned value will apply to all methods.
1993
+
1994
+ *Mock `:a` and `:b` and make them both to return `:x`:*
1995
+
1996
+ ```ruby
1997
+ mock(some_object, :a, :b) { :x }
1998
+ ```
1999
+
2000
+ There is no way to have specific setups when mocking multiple methods, that's it, all of them will behave the same way.
2001
+
2002
+ *Mock `:a` and `:b` and make them both to return `:one` when called with argument 1 and return `:two` when called with argument 2:*
2003
+
2004
+ ```ruby
2005
+ mock(some_object, :a, :b).
2006
+ with(1) { :one }.
2007
+ with(2) { :two }
2008
+ ```
2009
+
2010
+
2011
+ **Mocks visibility rules** works the same as for stubs. If some protected method are mocked, the mocked version will be protected as well. Same for private and public methods.
2012
+
2013
+ However when you need a mock to be of specific visibility, use one of `public_mock`, `protected_mock` or `private_mock`.
2014
+
2015
+ And of course there are their counterparts for multiple mocking: `public_mocks`, `protected_mocks` or `private_mocks`.
2016
+
2017
+
2018
+ One **significant difference** between mocks and stubs is that mocks does not support chained methods. That's it, you can not do like this: `mock(some_object, 'a.b.c')`. Instead you should stub the chain then explicitly define expectations:
2019
+
2020
+ ```ruby
2021
+ stub(some_object, 'a.b.c')
2022
+ expect(some_object).to_receive(:a)
2023
+ expect(some_object.a).to_receive(:b)
2024
+ # ...
2025
+ ```
2026
+
2027
+
2028
+ [&#8679; Table of Contents](#docs)
2029
+
2030
+
2031
+ ### Doubles
2032
+
2033
+ During testing you may need entities that behaves like some "real" objects.
2034
+
2035
+ Let's say you need to ensure a welcome email is sent to user after account creation.
2036
+
2037
+ Rather than create a full-blown mail object you can use a double that behaves like a mailer, e.g. responds to `deliver`.
2038
+
2039
+ To create a double simply use the `double` method:
2040
+
2041
+ ```ruby
2042
+ email = 'bob@bobsen.com'
2043
+
2044
+ # creating mailer double
2045
+ mailer = double(:mailer, deliver: true)
2046
+
2047
+ # ensuring `deliver` will be called with user's email
2048
+ expect(mailer).to_receive(:deliver).with(user.email)
2049
+
2050
+ # injecting double into system
2051
+ User.new!(email: email, mailer: mailer)
2052
+ ```
2053
+
2054
+ For this contrived test to pass, `mailer#deliver` should be called under the hood.
2055
+
2056
+ If one or more arguments given to `double` method, first argument will be used as name(unless it is a Hash). Double's name turns to be very helpful on failures output, so a real name will output rather than `#<Object...` notation.
2057
+
2058
+ [&#8679; Table of Contents](#docs)
2059
+
2060
+
2061
+ ## Running Specs
2062
+
2063
+ Minispec will look for specs in `./spec` and `./test` folders.
2064
+
2065
+ Any files that match `*_spec.rb`, `*_test.rb` or `test_*.rb` will be loaded by Minispec.
2066
+
2067
+ So if you go standard way and put name you spec files like this and put them in `spec` or `test` folder, all you need to run specs is to call `minispec` in you terminal:
2068
+
2069
+ ```bash
2070
+ $ minispec
2071
+ ```
2072
+
2073
+ If you want to test only some files, pass them as space delimited arguments:
2074
+
2075
+ ```bash
2076
+ $ minispec spec/user_spec.rb spec/cart_spec.rb
2077
+ ```
2078
+
2079
+ If you need to run specs from a script, use `Minispec.run`.
2080
+
2081
+ *Rakefile*
2082
+
2083
+ ```ruby
2084
+ require 'minispec'
2085
+ desc 'Run all tests'
2086
+ task :test do
2087
+ Minispec.run
2088
+ end
2089
+ ```
2090
+
2091
+ `run` accepts `pattern` option, so you can instruct Minispec on how to load your specs:
2092
+
2093
+
2094
+ ```ruby
2095
+ require 'minispec'
2096
+
2097
+ namespace :test do
2098
+
2099
+ desc 'Run user tests'
2100
+ task :users do
2101
+ Minispec.run(pattern: 'test/**/user*.rb')
2102
+ end
2103
+ end
2104
+ ```
2105
+
2106
+
2107
+ Also `:file` option accepted so you can run a single file:
2108
+
2109
+ ```ruby
2110
+ require 'minispec'
2111
+
2112
+ Dir['test/**/test_*.rb'].each do |file|
2113
+ name = file.sub(/test\/test_(.+)\.rb/, '\1')
2114
+ desc 'Run %s tests' % name
2115
+ task 'test:' + name do
2116
+ Minispec.run(file: file)
2117
+ end
2118
+ end
2119
+ ```
2120
+
2121
+
2122
+ [&#8679; Table of Contents](#docs)
2123
+
2124
+
2125
+ ## Contributors
2126
+
2127
+ Want to contribute? Great! Contributors highly wanted and welcome!
2128
+
2129
+ 1. Fork it
2130
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
2131
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
2132
+ 4. Push to the branch (`git push origin my-new-feature`)
2133
+ 5. Create new Pull Request
2134
+
2135
+
2136
+ ## License
2137
+
2138
+ Copyright &copy; 2014 Slee Woo &lt;mail@sleewoo.com&gt;
2139
+
2140
+ Distributed under the **[MIT License](https://github.com/sleewoo/minispec/blob/master/LICENSE)**