oktest 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c60ec7ce654d0f50f7f2cb6523e2f27f4e843b7e88e37d886a694553805f2d7e
4
+ data.tar.gz: 559d192eaa5a321a59db95defbc2e7e4ec271bc8d3b97aeba2b8658a61a1b0d9
5
+ SHA512:
6
+ metadata.gz: 73e39ea5796c682696127b34bff655870458b509aec487b2420276e8bd892a6a60c82a74895c0be89aa2160a231ef08eb92aff8b5a998ca335e735efbb410487
7
+ data.tar.gz: 8795c2227f46c7137d3456fa1040aaff0de3d020748c553439d39c7852a66cd0af7d81150cd26ef4c68907d503501de5c7cd3ec3eb3bfa6bdfe1bf2e4c9d5435
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011-2021 kuwata-lab.com all rights reserved
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,1856 @@
1
+ <!-- # -*- coding: utf-8 -*- -->
2
+ # Oktest.rb README
3
+
4
+
5
+ Oktest.rb is a new-style testing library for Ruby.
6
+
7
+ * `ok {actual} == expected` style assertion.
8
+ * **Fixture injection** inspired by dependency injection.
9
+ * Structured test specifications like RSpec.
10
+ * Filtering testcases by pattern or tags.
11
+ * Blue/red color instead of green/red for accesability.
12
+ * Small code size (about 2400 lines) and good performance.
13
+
14
+ ```ruby
15
+ ### Oktest ### Test::Unit
16
+ require 'oktest' # require 'test/unit'
17
+ #
18
+ Oktest.scope do #
19
+ #
20
+ topic "Example" do # class ExampleTest < Test::Unit::TestCase
21
+ #
22
+ spec "...description..." do # def test_1 # ...description...
23
+ ok {1+1} == 2 # assert_equal 2, 1+1
24
+ not_ok {1+1} == 3 # assert_not_equal 3, 1+1
25
+ ok {3*3} < 10 # assert 3*3 < 10
26
+ not_ok {3*4} < 10 # assert 3*4 >= 10
27
+ ok {@var}.nil? # assert_nil @var
28
+ not_ok {123}.nil? # assert_not_nil 123
29
+ ok {3.14}.in_delta?(3.1, 0.1) # assert_in_delta 3.1, 3.14, 0.1
30
+ ok {'aaa'}.is_a?(String) # assert_kind_of String, 'aaa'
31
+ ok {'123'} =~ (/\d+/) # assert_match /\d+/, '123'
32
+ ok {:sym}.same?(:sym) # assert_same? :sym, :sym
33
+ ok {'README.md'}.file_exist? # assert File.file?('README.md')
34
+ ok {'/tmp'}.dir_exist? # assert File.directory?('/tmp')
35
+ ok {'/blabla'}.not_exist? # assert !File.exist?('/blabla')
36
+ pr = proc { .... } # exc = assert_raise(Error) { .... }
37
+ ok {pr}.raise?(Error, "mesg") # assert_equal "mesg", exc.message
38
+ end # end
39
+ #
40
+ end # end
41
+ #
42
+ end #
43
+ ```
44
+
45
+ Oktest.rb requires Ruby 2.3 or later.
46
+
47
+
48
+
49
+ ## Table of Contents
50
+
51
+ <!-- TOC -->
52
+
53
+ * <a href="#quick-tutorial">Quick Tutorial</a>
54
+ * <a href="#install">Install</a>
55
+ * <a href="#basic-example">Basic Example</a>
56
+ * <a href="#assertion-failure-and-error">Assertion Failure, and Error</a>
57
+ * <a href="#skip-and-todo">Skip, and Todo</a>
58
+ * <a href="#reporting-style">Reporting Style</a>
59
+ * <a href="#run-all-test-scripts-under-directory">Run All Test Scripts Under Directory</a>
60
+ * <a href="#tag-and-filtering">Tag and Filtering</a>
61
+ * <a href="#case_when-and-case_else"><code>case_when</code> and <code>case_else</code></a>
62
+ * <a href="#optional-unary-operators">Optional: Unary Operators</a>
63
+ * <a href="#generate-test-code-skeleton">Generate Test Code Skeleton</a>
64
+ * <a href="#defining-methods-in-topics">Defining Methods in Topics</a>
65
+ * <a href="#assertions">Assertions</a>
66
+ * <a href="#basic-assertions">Basic Assertions</a>
67
+ * <a href="#predicate-assertions">Predicate Assertions</a>
68
+ * <a href="#negative-assertion">Negative Assertion</a>
69
+ * <a href="#exception-assertion">Exception Assertion</a>
70
+ * <a href="#custom-assertion">Custom Assertion</a>
71
+ * <a href="#fixtures">Fixtures</a>
72
+ * <a href="#setup-and-teardown">Setup and Teardown</a>
73
+ * <a href="#at_end-crean-up-handler"><code>at_end()</code>: Crean-up Handler</a>
74
+ * <a href="#named-fixtures">Named Fixtures</a>
75
+ * <a href="#fixture-injection">Fixture Injection</a>
76
+ * <a href="#global-scope">Global Scope</a>
77
+ * <a href="#helpers">Helpers</a>
78
+ * <a href="#capture_sio"><code>capture_sio()</code></a>
79
+ * <a href="#dummy_file"><code>dummy_file()</code></a>
80
+ * <a href="#dummy_dir"><code>dummy_dir()</code></a>
81
+ * <a href="#dummy_values"><code>dummy_values()</code></a>
82
+ * <a href="#dummy_attrs"><code>dummy_attrs()</code></a>
83
+ * <a href="#dummy_ivars"><code>dummy_ivars()</code></a>
84
+ * <a href="#recorder"><code>recorder()</code></a>
85
+ * <a href="#tips">Tips</a>
86
+ * <a href="#ok--in-minitest"><code>ok {}</code> in MiniTest</a>
87
+ * <a href="#testing-rack-application">Testing Rack Application</a>
88
+ * <a href="#traverser-class">Traverser Class</a>
89
+ * <a href="#benchmarks">Benchmarks</a>
90
+ * <a href="#--faster-option"><code>--faster</code> Option</a>
91
+ * <a href="#change-log">Change Log</a>
92
+ * <a href="#license-and-copyright">License and Copyright</a>
93
+
94
+ <!-- /TOC -->
95
+
96
+
97
+
98
+ ## Quick Tutorial
99
+
100
+
101
+ ### Install
102
+
103
+ ```terminal
104
+ ### install
105
+ $ gem install oktest
106
+ $ oktest --help
107
+
108
+ ### create test directory
109
+ $ mkdir test
110
+
111
+ ### create test script
112
+ $ oktest --create > test/example_test.rb
113
+ $ less test/example_test.rb
114
+
115
+ ### run test script
116
+ $ oktest -s verbose test
117
+ * Class
118
+ * #method_name()
119
+ - [pass] 1+1 should be 2.
120
+ - [pass] fixture injection examle.
121
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.001s
122
+ ```
123
+
124
+
125
+ ### Basic Example
126
+
127
+ test/example01_test.rb:
128
+
129
+ ```ruby
130
+ # coding: utf-8
131
+
132
+ require 'oktest'
133
+
134
+ class Hello
135
+ def hello(name="world")
136
+ return "Hello, #{name}!"
137
+ end
138
+ end
139
+
140
+
141
+ Oktest.scope do
142
+
143
+ topic Hello do
144
+
145
+ topic '#hello()' do
146
+
147
+ spec "returns greeting message." do
148
+ actual = Hello.new.hello()
149
+ ok {actual} == "Hello, world!"
150
+ end
151
+
152
+ spec "accepts user name." do
153
+ actual = Hello.new.hello("RWBY")
154
+ ok {actual} == "Hello, RWBY!"
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ ```
163
+
164
+ Result:
165
+
166
+ ```terminal
167
+ $ oktest test/example01_test.rb # or: ruby test/example01_test.rb
168
+ * Hello
169
+ * #hello()
170
+ - [pass] returns greeting message.
171
+ - [pass] accepts user name.
172
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
173
+ ```
174
+
175
+
176
+ ### Assertion Failure, and Error
177
+
178
+ test/example02_test.rb:
179
+
180
+ ```ruby
181
+ require 'oktest'
182
+
183
+ Oktest.scope do
184
+
185
+ topic 'other examples' do
186
+
187
+ spec "example of assertion failure" do
188
+ ok {1+1} == 2 # pass
189
+ ok {1+1} == 0 # FAIL
190
+ end
191
+
192
+ spec "example of something error" do
193
+ x = foobar # NameError
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ ```
200
+
201
+ Result:
202
+
203
+ ```terminal
204
+ $ oktest test/example02_test.rb # or: ruby test/example02_test.rb
205
+ * other examples
206
+ - [Fail] example of assertion failure
207
+ - [ERROR] example of something error
208
+ ----------------------------------------------------------------------
209
+ [Fail] other examples > example of assertion failure
210
+ tmp/test/example02_test.rb:9:in `block (3 levels) in <main>'
211
+ ok {1+1} == 0 # FAIL
212
+ $<actual> == $<expected>: failed.
213
+ $<actual>: 2
214
+ $<expected>: 0
215
+ ----------------------------------------------------------------------
216
+ [ERROR] other examples > example of something error
217
+ tmp/test/example02_test.rb:13:in `block (3 levels) in <main>'
218
+ x = foobar # NameError
219
+ NameError: undefined local variable or method `foobar' for #<#<Class:...>:...>
220
+ ----------------------------------------------------------------------
221
+ ## total:2 (pass:0, fail:1, error:1, skip:0, todo:0) in 0.000s
222
+ ```
223
+
224
+
225
+ ### Skip, and Todo
226
+
227
+ test/example03_test.rb:
228
+
229
+ ```ruby
230
+ require 'oktest'
231
+
232
+ Oktest.scope do
233
+
234
+ topic 'other examples' do
235
+
236
+ spec "example of skip" do
237
+ skip_when RUBY_VERSION < "3.0", "requires Ruby3"
238
+ ok {1+1} == 2
239
+ end
240
+
241
+ spec "example of todo" # spec without block means TODO
242
+
243
+ spec "example of todo (when passed unexpectedly)" do
244
+ TODO() # this spec should be failed,
245
+ # because not implemented yet.
246
+ ok {1+1} == 2 # thefore if all assesions passed,
247
+ # it means 'unexpected success'.
248
+ end
249
+
250
+ end
251
+
252
+ end
253
+ ```
254
+
255
+ Result:
256
+
257
+ ```terminal
258
+ $ oktest test/example03_test.rb # or: ruby test/example03_test.rb
259
+ * other examples
260
+ - [Skip] example of skip (reason: requires Ruby3)
261
+ - [TODO] example of todo
262
+ - [Fail] example of todo (when passed unexpectedly)
263
+ ----------------------------------------------------------------------
264
+ [Fail] other examples > example of todo (when passed unexpectedly)
265
+ test/example03_test.rb:14:in `block (2 levels) in <top (required)>'
266
+ spec "example of todo (when passed unexpectedly)" do
267
+ spec should be failed (because not implemented yet), but passed unexpectedly.
268
+ ----------------------------------------------------------------------
269
+ ## total:2 (pass:0, fail:1, error:0, skip:1, todo:1) in 0.000s
270
+ ```
271
+
272
+
273
+ ### Reporting Style
274
+
275
+ Verbose mode (default):
276
+
277
+ ```terminal
278
+ $ oktest test/example01_test.rb -s verbose # or -sv
279
+ * Hello
280
+ * #hello()
281
+ - [pass] returns greeting message.
282
+ - [pass] accepts user name.
283
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
284
+ ```
285
+
286
+ Simple mode:
287
+
288
+ ```terminal
289
+ $ oktest test/example01_test.rb -s simple # or -ss
290
+ test/example01_test.rb: ..
291
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
292
+ ```
293
+
294
+ Plain mode:
295
+
296
+ ```terminal
297
+ $ oktest test/example01_test.rb -s simple # or -ss
298
+ ..
299
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
300
+ ```
301
+
302
+ Quiet mode:
303
+
304
+ ```terminal
305
+ $ oktest test/example01_test.rb -s quiet # or -sq
306
+
307
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
308
+ ```
309
+
310
+ Quiet mode reports progress only of failed or error test cases (and doesn't
311
+ report progress of passed ones), so it's output is very compact. This is
312
+ very useful for large project which contains large number of test cases.
313
+
314
+
315
+ ### Run All Test Scripts Under Directory
316
+
317
+ How to run test scripts under `test` directory:
318
+
319
+ ```terminal
320
+ $ ls test
321
+ example01_test.rb example02_test.rb example03_test.rb
322
+
323
+ $ oktest -s simple test # or: ruby -r oktest -e 'Oktest.main' -- test -s simple
324
+ tmp/test/example01_test.rb: ..
325
+ tmp/test/example02_test.rb: fE
326
+ ----------------------------------------------------------------------
327
+ [Fail] other examples > example of assertion failure
328
+ tmp/test/example02_test.rb:9:in `block (3 levels) in <top (required)>'
329
+ ok {1+1} == 0 # FAIL
330
+ -e:1:in `<main>'
331
+ $<actual> == $<expected>: failed.
332
+ $<actual>: 2
333
+ $<expected>: 0
334
+ ----------------------------------------------------------------------
335
+ [ERROR] other examples > example of something error
336
+ tmp/test/example02_test.rb:13:in `block (3 levels) in <top (required)>'
337
+ x = foobar # NameError
338
+ -e:1:in `<main>'
339
+ NameError: undefined local variable or method `foobar' for #<#<Class:...>:...>
340
+ ----------------------------------------------------------------------
341
+ tmp/test/example03_test.rb: st
342
+ ## total:6 (pass:2, fail:1, error:1, skip:1, todo:1) in 0.000s
343
+ ```
344
+
345
+ Test script filename should be `test_xxx.rb` or `xxx_test.rb`
346
+ (not `test-xxx.rb` nor `xxx-test.rb`).
347
+
348
+
349
+ ### Tag and Filtering
350
+
351
+ `topic()` and `spec()` accepts tag name, for example 'obsolete' or 'experimental'.
352
+
353
+ test/example04_test.rb:
354
+
355
+ ```ruby
356
+ require 'oktest'
357
+
358
+ Oktest.scope do
359
+
360
+ topic 'Example topic' do
361
+
362
+ topic Integer do
363
+ spec "example #1" do
364
+ ok {1+1} == 2
365
+ end
366
+ spec "example #2", tag: 'old' do # tag name: 'old'
367
+ ok {1-1} == 0
368
+ end
369
+ end
370
+
371
+ topic Float, tag: 'exp' do # tag name: 'exp'
372
+ spec "example #3" do
373
+ ok {1.0+1.0} == 2.0
374
+ end
375
+ spec "example #4" do
376
+ ok {1.0-1.0} == 0.0
377
+ end
378
+ end
379
+
380
+ topic String, tag: ['exp', 'old'] do # tag name: 'old' and 'exp'
381
+ spec "example #5" do
382
+ ok {'a'*3} == 'aaa'
383
+ end
384
+ end
385
+
386
+ end
387
+
388
+ end
389
+ ```
390
+
391
+ It is possible to filter topics and specs by tag name (pattern).
392
+
393
+ ```terminal
394
+ $ oktest -F tag=exp tests/ # filter by tag name
395
+ $ oktest -F tag='*exp*' tests/ # filter by tag name pattern
396
+ $ oktest -F tag='{exp,old}' tests/ # filter by multiple tag names
397
+ ```
398
+
399
+ It is also possible to filter topics or specs by name.
400
+ Pattern (!= regular expression) supports `*`, `?`, `[]`, and `{}`.
401
+
402
+ ```terminal
403
+ $ oktest -F topic='*Integer*' test/ # filter topics by pattern
404
+ $ oktest -F spec='*#[1-3]' test/ # filter specs by pattern
405
+ ```
406
+
407
+ If you need negative filter, use `!=` instead of `=`.
408
+
409
+ ```terminal
410
+ $ oktest -F spec!='*#5' tests/ # exclude spec 'example #5'
411
+ $ oktest -F tag!='{exp,old}' tests/ # exclude tag='exp' or tag='old'
412
+ ```
413
+
414
+
415
+ ### `case_when` and `case_else`
416
+
417
+ `case_when` and `case_else` represents conditional spec.
418
+
419
+ test/example05_test.rb:
420
+
421
+ ```ruby
422
+ require 'oktest'
423
+
424
+ Oktest.scope do
425
+ topic Integer do
426
+ topic '#abs()' do
427
+
428
+ case_when "value is negative..." do
429
+ spec "converts value into positive." do
430
+ ok {-123.abs()} == 123
431
+ end
432
+ end
433
+
434
+ case_when "value is zero..." do
435
+ spec "returns zero." do
436
+ ok {0.abs()} == 0
437
+ end
438
+ end
439
+
440
+ case_else do
441
+ spec "returns itself." do
442
+ ok {123.abs()} == 123
443
+ end
444
+ end
445
+
446
+ end
447
+ end
448
+ end
449
+ ```
450
+
451
+ Result:
452
+
453
+ ```terminal
454
+ $ ruby test/example05_test.rb
455
+ * Integer
456
+ * #abs()
457
+ - When value is negative...
458
+ - [pass] converts value into positive.
459
+ - When value is zero...
460
+ - [pass] returns zero.
461
+ - Else
462
+ - [pass] returns itself.
463
+ ## total:3 (pass:3, fail:0, error:0, skip:0, todo:0) in 0.001s
464
+ ```
465
+
466
+
467
+ ### Optional: Unary Operators
468
+
469
+ `topic()` accepts unary `+` operator and `spec()` accepts unary `-` operator.
470
+ This makes test scripts more readable.
471
+
472
+ <!--
473
+ test/example06_test.rb:
474
+ -->
475
+
476
+ ```ruby
477
+ require 'oktest'
478
+
479
+ Oktest.scope do
480
+
481
+ + topic('example') do # unary `+` operator
482
+
483
+ + topic('example') do # unary `+` operator
484
+
485
+ - spec("1+1 is 2.") do # unary `-` operator
486
+ ok {1+1} == 2
487
+ end
488
+
489
+ - spec("1*1 is 1.") do # unary `-` operator
490
+ ok {1*1} == 1
491
+ end
492
+
493
+ end
494
+
495
+ end
496
+
497
+ end
498
+ ```
499
+
500
+
501
+ ### Generate Test Code Skeleton
502
+
503
+ `oktest -G` (or `oktest --generate`) generates test code skeleton from ruby file.
504
+ Comment line starting with `#;` is regarded as spec description.
505
+
506
+ hello.rb:
507
+
508
+ ```ruby
509
+ class Hello
510
+
511
+ def hello(name=nil)
512
+ #; default name is 'world'.
513
+ if name.nil?
514
+ name = "world"
515
+ end
516
+ #; returns greeting message.
517
+ return "Hello, #{name}!"
518
+ end
519
+
520
+ end
521
+ ```
522
+
523
+ Generate test code skeleton:
524
+
525
+ ```terminal
526
+ $ oktest -G hello.rb > test/hello_test.rb
527
+ ```
528
+
529
+ test/hello_test.rb:
530
+
531
+ ```ruby
532
+ # coding: utf-8
533
+
534
+ require 'oktest'
535
+
536
+ Oktest.scope do
537
+
538
+
539
+ topic Hello do
540
+
541
+
542
+ topic '#hello()' do
543
+
544
+ spec "default name is 'world'."
545
+
546
+ spec "returns greeting message."
547
+
548
+ end # #hello()
549
+
550
+
551
+ end # Hello
552
+
553
+
554
+ end
555
+ ```
556
+
557
+ (Experimental) `--generate=unaryop` generates test skeleton with unary operator `+` and `-`.
558
+
559
+ ```terminal
560
+ $ oktest --generate=unaryop hello.rb > test/hello2_test.rb
561
+ ```
562
+
563
+ test/hello2_test.rb:
564
+
565
+ ```ruby
566
+ # coding: utf-8
567
+
568
+ require 'oktest'
569
+
570
+ Oktest.scope do
571
+
572
+
573
+ + topic(Hello) do
574
+
575
+
576
+ + topic('#hello()') do
577
+
578
+ - spec("default name is 'world'.")
579
+
580
+ - spec("returns greeting message.")
581
+
582
+ end # #hello()
583
+
584
+
585
+ end # Hello
586
+
587
+
588
+ end
589
+ ```
590
+
591
+
592
+ ### Defining Methods in Topics
593
+
594
+ Methods defined in topics can be called in specs.
595
+
596
+ <!--
597
+ test/example08a_test.rb:
598
+ -->
599
+
600
+ ```ruby
601
+ require 'oktest'
602
+ Oktest.scope do
603
+
604
+ topic "Method example" do
605
+
606
+ def hello() # define method in topic block
607
+ return "Hello!"
608
+ end
609
+
610
+ spec "example" do
611
+ s = hello() # call method in spec block
612
+ ok {s} == "Hello!"
613
+ end
614
+
615
+ end
616
+
617
+ end
618
+ ```
619
+
620
+ * It is OK to call methods defined in parent topics.
621
+ * It will be ERROR to call methods defined in child topics.
622
+
623
+ <!--
624
+ test/example08b_test.rb:
625
+ -->
626
+
627
+ ```ruby
628
+ require 'oktest'
629
+ Oktest.scope do
630
+
631
+ + topic('Outer') do
632
+
633
+ + topic('Middle') do
634
+
635
+ def hello() # define method in topic block
636
+ return "Hello!"
637
+ end
638
+
639
+ + topic('Inner') do
640
+
641
+ - spec("inner spec") do
642
+ s = hello() # OK: call method defined in parent topic
643
+ ok {s} == "Hello!"
644
+ end
645
+
646
+ end
647
+
648
+ end
649
+
650
+ - spec("outer spec") do
651
+ s = hello() # ERROR: call method defined in child topic
652
+ ok {x} == "Hello!"
653
+ end
654
+
655
+ end
656
+
657
+ end
658
+ ```
659
+
660
+
661
+
662
+ ## Assertions
663
+
664
+
665
+ ### Basic Assertions
666
+
667
+ In the following example, `a` means actual value and `e` means expected value.
668
+
669
+ <!--
670
+ test/example11_test.rb:
671
+ -->
672
+
673
+ ```ruby
674
+ ok {a} == e # fail unless a == e
675
+ ok {a} != e # fail unless a != e
676
+ ok {a} === e # fail unless a === e
677
+ ok {a} !== e # fail unless a !== e
678
+
679
+ ok {a} > e # fail unless a > e
680
+ ok {a} >= e # fail unless a >= e
681
+ ok {a} < e # fail unless a < e
682
+ ok {a} <= e # fail unless a <= e
683
+
684
+ ok {a} =~ e # fail unless a =~ e
685
+ ok {a} !~ e # fail unless a !~ e
686
+
687
+ ok {a}.same?(e) # fail unless a.equal?(e)
688
+ ok {a}.in?(e) # fail unless e.include?(a)
689
+ ok {a}.in_delta?(e, x) # fail unless e-x < a < e+x
690
+ ok {a}.truthy? # fail unless !!a == true
691
+ ok {a}.falsy? # fail unless !!a == false
692
+
693
+ ok {a}.file_exist? # fail unless File.file?(a)
694
+ ok {a}.dir_exist? # fail unless File.directory?(a)
695
+ ok {a}.symlink_exist? # fail unless File.symlink?(a)
696
+ ok {a}.not_exist? # fail unless ! File.exist?(a)
697
+
698
+ ok {a}.attr(name, e) # fail unless a.__send__(name) == e
699
+ ok {a}.keyval(key, e) # fail unless a[key] == e
700
+ ok {a}.item(key, e) # alias of `ok {a}.keyval(key, e)`
701
+ ok {a}.length(e) # fail unless a.length == e
702
+ ```
703
+
704
+ It is possible to chan method call of `.attr()` and `.keyval()`.
705
+
706
+ <!--
707
+ test/example11b_test.rb:
708
+ -->
709
+
710
+ ```ruby
711
+ ok {a}.attr(:name1, 'val1').attr(:name2, 'val2').attr(:name3, 'val3')
712
+ ok {a}.keyval(:key1, 'val1').keyval(:key2, 'val2').keyval(:key3, 'val3')
713
+ ```
714
+
715
+
716
+ ### Predicate Assertions
717
+
718
+ `ok {}` handles predicate methods (such as `.nil?`, `.empty?`, or `.key?`) automatically.
719
+
720
+ <!--
721
+ test/example12_test.rb:
722
+ -->
723
+
724
+ ```ruby
725
+ ok {a}.nil? # same as ok {a.nil?} == true
726
+ ok {a}.empty? # same as ok {a.empty?} == true
727
+ ok {a}.key?(e) # same as ok {a.key?(e)} == true
728
+ ok {a}.is_a?(e) # same as ok {a.is_a?(e)} == true
729
+ ok {a}.include?(e) # same as ok {a.include?(e)} == true
730
+ ok {a}.between?(x, y) # same as ok {a.between?(x, y)} == true
731
+ ```
732
+
733
+ `Pathname()` is a good example of predicate methods.
734
+ See [pathname.rb](https://ruby-doc.org/stdlib-2.7.0/libdoc/pathname/rdoc/Pathname.html)
735
+ document for details about `Pathname()`.
736
+
737
+ <!--
738
+ test/example12b_test.rb:
739
+ -->
740
+
741
+ ```ruby
742
+ require 'pathname' # !!!!!
743
+
744
+ ok {Pathname(a)}.owned? # same as ok {Pathname(a).owned?} == true
745
+ ok {Pathname(a)}.readable? # same as ok {Pathname(a).readable?} == true
746
+ ok {Pathname(a)}.writable? # same as ok {Pathname(a).writable?} == true
747
+ ok {Pathname(a)}.absolute? # same as ok {Pathname(a).absolute?} == true
748
+ ok {Pathname(a)}.relative? # same as ok {Pathname(a).relative?} == true
749
+ ```
750
+
751
+
752
+ ### Negative Assertion
753
+
754
+ <!--
755
+ test/example13_test.rb:
756
+ -->
757
+
758
+ ```ruby
759
+ not_ok {a} == e # fail if a == e
760
+ ok {a}.NOT == e # fail if a == e
761
+
762
+ not_ok {a}.file_exist? # fail if File.file?(a)
763
+ ok {a}.NOT.file_exist? # fail if File.file?(a)
764
+ ```
765
+
766
+
767
+ ### Exception Assertion
768
+
769
+ If you want to assert whether exception raised or not:
770
+
771
+ <!--
772
+ test/example14_test.rb:
773
+ -->
774
+
775
+ ```ruby
776
+ pr = proc do
777
+ "abc".len() # raises NoMethodError
778
+ end
779
+ ok {pr}.raise?(NoMethodError)
780
+ ok {pr}.raise?(NoMethodError, "undefined method `len' for \"abc\":String")
781
+ ok {pr}.raise?(NoMethodError, /^undefined method `len'/)
782
+ ok {pr}.raise? # pass if any exception raised, fail if nothing raised
783
+
784
+ ## get exception object
785
+ ok {pr}.raise?(NoMethodError) {|exc|
786
+ ok {exc.class} == NoMethodError
787
+ ok {exc.message} == "undefined method `len' for \"abc\":String"
788
+ }
789
+
790
+ ## assert that procedure does NOT raise any exception
791
+ ok {pr}.NOT.raise? # no exception class nor error message
792
+ not_ok {pr}.raise? # same as above
793
+
794
+ ## assert that procedure throws symbol.
795
+ pr2 = proc do
796
+ throw :quit
797
+ end
798
+ ok {pr2}.throw?(:quit) # pass if :quit thrown, fail if other or nothing thrown
799
+ ```
800
+
801
+ If procedure contains `raise "errmsg"` instead of `raise ErrorClass, "errmsg"`,
802
+ you can omit exception class such as `ok {pr}.raise?("errmsg")`.
803
+
804
+ <!--
805
+ test/example14b_test.rb:
806
+ -->
807
+
808
+ ```ruby
809
+ pr = proc do
810
+ raise "something wrong" # !!! error class not specified !!!
811
+ end
812
+ ok {pr}.raise?("something wrong") # !!! error class not specified !!!
813
+ ```
814
+
815
+ Notice that `ok().raise?()` compares error class by `==` operator, not `.is_a?` method.
816
+
817
+ <!--
818
+ test/example14c_test.rb:
819
+ -->
820
+
821
+ ```ruby
822
+ pr = proc { 1/0 } # raises ZeroDivisionError
823
+
824
+ ok {pr}.raise?(ZeroDivisoinError) # pass
825
+ ok {pr}.raise?(StandardError) # ERROR: ZeroDivisionError raised
826
+ ok {pr}.raise?(Exception) # ERROR: ZeroDivisionError raised
827
+ ```
828
+
829
+ This is an intended design to avoid unexpected assertion success.
830
+ For example, `assert_raises(NameError) { .... }` in MiniTest will result in
831
+ success unexpectedly even if `NoMethodError` raised in the block, because
832
+ `NoMethodError` is a subclass of `NameError`.
833
+
834
+ <!--
835
+ test/example14d_test.rb:
836
+ -->
837
+
838
+ ```ruby
839
+ require 'minitest/spec'
840
+ require 'minitest/autorun'
841
+
842
+ describe "assert_raise()" do
843
+ it "results in success unexpectedly" do
844
+ ## catches NameError and it's subclasses, including NoMethodError.
845
+ assert_raises(NameError) do # catches NoMethodError, too.
846
+ "str".foobar() # raises NoMethodError.
847
+ end
848
+ end
849
+ end
850
+ ```
851
+
852
+ Oktest.rb can avoid this pitfall, because `.raise?()` compares error class
853
+ by `==` operator, not `.is_a?` method.
854
+
855
+ <!--
856
+ test/example14e_test.rb:
857
+ -->
858
+
859
+ ```ruby
860
+ require 'oktest'
861
+
862
+ Oktest.scope do
863
+ topic 'ok().raise?' do
864
+ spec "doesn't catch subclasses." do
865
+ pr = proc do
866
+ "str".foobar() # raises NoMethodError
867
+ end
868
+ ok {pr}.raise?(NoMethodError) # pass
869
+ ok {pr}.raise?(NameError) # NoMethodError raised intendedly
870
+ end
871
+ end
872
+ end
873
+ ```
874
+
875
+ To catch subclass of error class, invoke `.raise!` instead of `.raise?`.
876
+ For example: `ok {pr}.raise!(NameError, /foobar/, subclass: true)`.
877
+
878
+ <!--
879
+ test/example14f_test.rb:
880
+ -->
881
+
882
+ ```ruby
883
+ require 'oktest'
884
+
885
+ Oktest.scope do
886
+ topic 'ok().raise!' do
887
+ spec "catches subclasses." do
888
+ pr = proc do
889
+ "str".foobar() # raises NoMethodError
890
+ end
891
+ ok {pr}.raise!(NoMethodError) # pass
892
+ ok {pr}.raise!(NameError) # pass !!!!!
893
+ end
894
+ end
895
+ end
896
+ ```
897
+
898
+
899
+ ### Custom Assertion
900
+
901
+ How to define custom assertion:
902
+
903
+ <!--
904
+ test/example15_test.rb:
905
+ -->
906
+ ```ruby
907
+ require 'oktest'
908
+
909
+ Oktest::AssertionObject.class_eval do
910
+ def readable? # custom assertion: file readable?
911
+ _done()
912
+ result = File.readable?(@actual)
913
+ __assert(result == @bool) {
914
+ "File.readable?($<actual>) == #{@bool}: failed.\n" +
915
+ " $<actual>: #{@actual.inspect}"
916
+ }
917
+ self
918
+ end
919
+ end
920
+
921
+ Oktest.scope do
922
+
923
+ topic "Custom assertion" do
924
+
925
+ spec "example spec" do
926
+ ok {__FILE__}.readable? # custom assertion
927
+ end
928
+
929
+ end
930
+
931
+ end
932
+ ```
933
+
934
+
935
+
936
+ ## Fixtures
937
+
938
+
939
+ ### Setup and Teardown
940
+
941
+ test/example21a_test.rb:
942
+
943
+ ```ruby
944
+ require 'oktest'
945
+
946
+ Oktest.scope do
947
+
948
+ topic "Fixture example" do
949
+
950
+ before do # equivarent to setUp()
951
+ puts "=== before() ==="
952
+ end
953
+
954
+ after do # equivarent to tearDown()
955
+ puts "=== after() ==="
956
+ end
957
+
958
+ before_all do # equivarent to setUpAll()
959
+ puts "*** before_all() ***"
960
+ end
961
+
962
+ after_all do # equvarent to tearDownAll()
963
+ puts "*** after_all() ***"
964
+ end
965
+
966
+ spec "example spec #1" do
967
+ puts "---- example spec #1 ----"
968
+ end
969
+
970
+ spec "example spec #2" do
971
+ puts "---- example spec #2 ----"
972
+ end
973
+
974
+ end
975
+
976
+ end
977
+ ```
978
+
979
+ Result:
980
+
981
+ ```terminal
982
+ $ oktest -s plain test/example21_test.rb
983
+ *** before_all() ***
984
+ === before() ===
985
+ ---- example spec #1 ----
986
+ === after() ===
987
+ .=== before() ===
988
+ ---- example spec #2 ----
989
+ === after() ===
990
+ .*** after_all() ***
991
+
992
+ ## total:2 (pass:2, fail:0, error:0, skip:0, todo:0) in 0.000s
993
+ ```
994
+
995
+ These blocks can be defined per `topic()` or `scope()`.
996
+
997
+ * `before` block in outer topic/scope is called prior to `before` block in inner topic.
998
+ * `after` block in inner topic is called prior to `after` block in outer topic/scope.
999
+
1000
+ test/example21b_test.rb:
1001
+
1002
+ ```ruby
1003
+ require 'oktest'
1004
+
1005
+ Oktest.scope do
1006
+
1007
+ topic 'Outer' do
1008
+ before { puts "=== Outer: before ===" } # !!!!!
1009
+ after { puts "=== Outer: after ===" } # !!!!!
1010
+
1011
+ topic 'Middle' do
1012
+ before { puts "==== Middle: before ====" } # !!!!!
1013
+ after { puts "==== Middle: after ====" } # !!!!!
1014
+
1015
+ topic 'Inner' do
1016
+ before { puts "===== Inner: before =====" } # !!!!!
1017
+ after { puts "===== Inner: after =====" } # !!!!!
1018
+
1019
+ spec "example" do
1020
+ ok {1+1} == 2
1021
+ end
1022
+
1023
+ end
1024
+
1025
+ end
1026
+
1027
+ end
1028
+
1029
+ end
1030
+ ```
1031
+
1032
+ Result:
1033
+
1034
+ ```terminal
1035
+ $ oktest -s plain test/example21b_test.rb
1036
+ === Outer: before ===
1037
+ ==== Middle: before ====
1038
+ ===== Inner: before =====
1039
+ ===== Inner: after =====
1040
+ ==== Middle: after ====
1041
+ === Outer: after ===
1042
+ .
1043
+ ## total:1 (pass:1, fail:0, error:0, skip:0, todo:0) in 0.000s
1044
+ ```
1045
+
1046
+ If something error raised in `before`/`after`/`before_all`/`after_all` blocks,
1047
+ test script execution will be stopped instantly.
1048
+
1049
+
1050
+ ### `at_end()`: Crean-up Handler
1051
+
1052
+ It is possible to register clean-up operation with `at_end()`.
1053
+
1054
+ test/example22_test.rb:
1055
+
1056
+ ```ruby
1057
+ require 'oktest'
1058
+
1059
+ Oktest.scope do
1060
+
1061
+ topic "Auto clean-up" do
1062
+
1063
+ spec "example spec" do
1064
+ tmpfile = "tmp123.txt"
1065
+ File.write(tmpfile, "foobar\n")
1066
+ at_end do # register clean-up operation
1067
+ File.unlink(tmpfile)
1068
+ end
1069
+ #
1070
+ ok {tmpfile}.file_exist?
1071
+ end
1072
+
1073
+ end
1074
+
1075
+ end
1076
+ ```
1077
+
1078
+ * `at_end()` can be called multiple times.
1079
+ * Registered blocks are invoked in reverse order at end of test case.
1080
+ * Registered blocks of `at_end()` are invoked prior to block of `after()`.
1081
+ * If something error raised in `at_end()`, test script execution will be
1082
+ stopped instantly.
1083
+
1084
+
1085
+ ### Named Fixtures
1086
+
1087
+ `fixture() { ... }` in topic or scope block defines fixture builder,
1088
+ and `fixture()` in scope block returns fixture data.
1089
+
1090
+ test/example23_test.rb:
1091
+
1092
+ ```ruby
1093
+ require 'oktest'
1094
+
1095
+ Oktest.scope do
1096
+
1097
+ fixture :alice do # define fixture
1098
+ {name: "Alice"}
1099
+ end
1100
+
1101
+ fixture :bob do # define fixture
1102
+ {name: "Bob"}
1103
+ end
1104
+
1105
+ topic "Named fixture" do
1106
+
1107
+ spec "example spec" do
1108
+ alice = fixture(:alice) # create fixture object
1109
+ bob = fixture(:bob) # create fixture object
1110
+ ok {alice[:name]} == "Alice"
1111
+ ok {bob[:name]} == "Bob"
1112
+ end
1113
+
1114
+ end
1115
+
1116
+ end
1117
+ ```
1118
+
1119
+ Fixture block can have parameters.
1120
+
1121
+ test/example24_test.rb:
1122
+
1123
+ ```ruby
1124
+ require 'oktest'
1125
+
1126
+ Oktest.scope do
1127
+
1128
+ fixture :team do |mem1, mem2| # define fixture with block params
1129
+ {members: [mem1, mem2]}
1130
+ end
1131
+
1132
+ topic "Named fixture with args" do
1133
+
1134
+ spec "example spec" do
1135
+ alice = {name: "Alice"}
1136
+ bob = {name: "Bob"}
1137
+ team = fixture(:team, alice, bob) # create fixture with args
1138
+ ok {team[:members][0][:name]} == "Alice"
1139
+ ok {team[:members][1][:name]} == "Bob"
1140
+ end
1141
+
1142
+ end
1143
+
1144
+ end
1145
+ ```
1146
+
1147
+ * Fixtures can be defined in block of `topic()` as well as block of `Object.scope()`.
1148
+ * If fixture requires clean-up operation, call `at_end()` in `fixture()` block.
1149
+
1150
+ ```ruby
1151
+ fixture :tmpfile do
1152
+ tmpfile = "tmp#{rand().to_s[2..5]}.txt"
1153
+ File.write(tmpfile, "foobar\n", encoding: 'utf-8')
1154
+ at_end { File.unlink(tmpfile) if File.exist?(tmpfile) } # !!!!!
1155
+ tmpfile
1156
+ end
1157
+ ```
1158
+
1159
+
1160
+ ### Fixture Injection
1161
+
1162
+ Block parameters of `spec()` or `fixture()` represents fixture name, and
1163
+ Oktest.rb injects fixture objects into that parameters automatically.
1164
+
1165
+ test/example25_test.rb:
1166
+
1167
+ ```ruby
1168
+ require 'oktest'
1169
+
1170
+ Oktest.scope do
1171
+
1172
+ fixture :alice do # define fixture
1173
+ {name: "Alice"}
1174
+ end
1175
+
1176
+ fixture :bob do # define fixture
1177
+ {name: "Bob"}
1178
+ end
1179
+
1180
+ fixture :team do |alice, bob| # !!! fixture injection !!!
1181
+ {members: [alice, bob]}
1182
+ end
1183
+
1184
+ topic "Fixture injection" do
1185
+
1186
+ spec "example spec" do
1187
+ |alice, bob, team| # !!! fixture injection !!!
1188
+ ok {alice[:name]} == "Alice"
1189
+ ok {bob[:name]} == "Bob"
1190
+ #
1191
+ ok {team[:members]}.length(2)
1192
+ ok {team[:members][0]} == {name: "Alice"}
1193
+ ok {team[:members][1]} == {name: "Bob"}
1194
+ end
1195
+
1196
+ end
1197
+
1198
+ end
1199
+ ```
1200
+
1201
+ <!--
1202
+ * Special fixture name `this_topic` represents the first argument of `topic()`.
1203
+ * Special fixture name `this_spec` represents the description of `spec()`.
1204
+ -->
1205
+
1206
+
1207
+ ### Global Scope
1208
+
1209
+ It is a good idea to separate common fixtures into dedicated file.
1210
+ In this case, use `Oktest.global_scope()` instead of `Oktest.scope()`.
1211
+
1212
+ test/common_fixtures.rb:
1213
+
1214
+ ```ruby
1215
+ require 'oktest'
1216
+
1217
+ ## define common fixtures in global scope
1218
+ Oktest.global_scope do # !!!!!
1219
+
1220
+ fixture :alice do
1221
+ {name: "Alice"}
1222
+ end
1223
+
1224
+ fixture :bob do
1225
+ {name: "Bob"}
1226
+ end
1227
+
1228
+ fixture :team do |alice, bob|
1229
+ {members: [alice, bob]}
1230
+ end
1231
+
1232
+ end
1233
+ ```
1234
+
1235
+
1236
+
1237
+ ## Helpers
1238
+
1239
+
1240
+ ### `capture_sio()`
1241
+
1242
+ `capture_sio()` captures standard I/O.
1243
+
1244
+ test/example31_test.rb:
1245
+
1246
+ ```ruby
1247
+ require 'oktest'
1248
+
1249
+ Oktest.scope do
1250
+
1251
+ topic "Capturing" do
1252
+
1253
+ spec "example spec" do
1254
+ data = nil
1255
+ sout, serr = capture_sio("blabla") do # !!!!!
1256
+ data = $stdin.read() # read from stdin
1257
+ puts "fooo" # write into stdout
1258
+ $stderr.puts "baaa" # write into stderr
1259
+ end
1260
+ ok {data} == "blabla"
1261
+ ok {sout} == "fooo\n"
1262
+ ok {serr} == "baaa\n"
1263
+ end
1264
+
1265
+ end
1266
+
1267
+ end
1268
+ ```
1269
+
1270
+ * First argument of `capture_sio()` represents data from `$stdin`.
1271
+ If it is not necessary, you can omit it like `caputre_sio() do ... end`.
1272
+ * If you need `$stdin.tty? == true` and `$stdout.tty? == true`,
1273
+ call `capture_sio(tty: true) do ... end`.
1274
+
1275
+
1276
+ ### `dummy_file()`
1277
+
1278
+ `dummy_file()` creates dummy file temporarily.
1279
+
1280
+ test/example32_test.rb:
1281
+
1282
+ ```ruby
1283
+ require 'oktest'
1284
+
1285
+ Oktest.scope do
1286
+
1287
+ topic "dummy_file()" do
1288
+
1289
+ spec "usage #1: without block" do
1290
+ tmpfile = dummy_file("_tmp_file.txt", "blablabla") # !!!!!
1291
+ ok {tmpfile} == "_tmp_file.txt"
1292
+ ok {tmpfile}.file_exist?
1293
+ ## dummy file will be removed automatically at end of spec block.
1294
+ end
1295
+
1296
+ spec "usage #2: with block" do
1297
+ result = dummy_file("_tmp_file.txt", "blabla") do |tmpfile| # !!!!!
1298
+ ok {tmpfile} == "_tmp_file.txt"
1299
+ ok {tmpfile}.file_exist?
1300
+ ## dummy file will be removed automatically at end of this block.
1301
+ ## last value of block will be the return value of dummy_file().
1302
+ 1234
1303
+ end
1304
+ ok {result} == 1234
1305
+ ok {"_tmp_file.txt"}.not_exist?
1306
+ end
1307
+
1308
+ end
1309
+
1310
+ end
1311
+ ```
1312
+
1313
+ * If first argument of `dummy_file()` is nil, then it generates temporary file name automatically.
1314
+
1315
+
1316
+ ### `dummy_dir()`
1317
+
1318
+ `dummy_dir()` creates dummy directory temporarily.
1319
+
1320
+ test/example33_test.rb:
1321
+
1322
+ ```ruby
1323
+ require 'oktest'
1324
+
1325
+ Oktest.scope do
1326
+
1327
+ topic "dummy_dir()" do
1328
+
1329
+ spec "usage #1: without block" do
1330
+ tmpdir = dummy_dir("_tmp_dir") # !!!!!
1331
+ ok {tmpdir} == "_tmp_dir"
1332
+ ok {tmpdir}.dir_exist?
1333
+ ## dummy directory will be removed automatically at end of spec block
1334
+ ## even if it contais other files or directories.
1335
+ end
1336
+
1337
+ spec "usage #2: with block" do
1338
+ result = dummy_dir("_tmp_dir") do |tmpdir| # !!!!!
1339
+ ok {tmpdir} == "_tmp_dir"
1340
+ ok {tmpdir}.dir_exist?
1341
+ ## dummy directory will be removed automatically at end of this block
1342
+ ## even if it contais other files or directories.
1343
+ ## last value of block will be the return value of dummy_dir().
1344
+ 2345
1345
+ end
1346
+ ok {result} == 2345
1347
+ ok {"_tmp_dir"}.not_exist?
1348
+ end
1349
+
1350
+ end
1351
+
1352
+ end
1353
+ ```
1354
+
1355
+ * If first argument of `dummy_dir()` is nil, then it generates temorary directory name automatically.
1356
+
1357
+
1358
+ ### `dummy_values()`
1359
+
1360
+ `dummy_values()` changes hash values temporarily.
1361
+
1362
+ test/example34_test.rb:
1363
+
1364
+ ```ruby
1365
+ require 'oktest'
1366
+
1367
+ Oktest.scope do
1368
+
1369
+ topic "dummy_values()" do
1370
+
1371
+ spec "usage #1: without block" do
1372
+ hashobj = {:a=>1, 'b'=>2, :c=>3} # `:x` is not a key
1373
+ ret = dummy_values(hashobj, :a=>100, 'b'=>200, :x=>900) # !!!!!
1374
+ ok {hashobj[:a]} == 100
1375
+ ok {hashobj['b']} == 200
1376
+ ok {hashobj[:c]} == 3
1377
+ ok {hashobj[:x]} == 900
1378
+ ok {ret} == {:a=>100, 'b'=>200, :x=>900}
1379
+ ## values of hash object are recovered at end of spec block.
1380
+ end
1381
+
1382
+ spec "usage #2: with block" do
1383
+ hashobj = {:a=>1, 'b'=>2, :c=>3} # `:x` is not a key
1384
+ ret = dummy_values(hashobj, :a=>100, 'b'=>200, :x=>900) do |keyvals| # !!!!!
1385
+ ok {hashobj[:a]} == 100
1386
+ ok {hashobj['b']} == 200
1387
+ ok {hashobj[:c]} == 3
1388
+ ok {hashobj[:x]} == 900
1389
+ ok {keyvals} == {:a=>100, 'b'=>200, :x=>900}
1390
+ ## values of hash object are recovered at end of this block.
1391
+ ## last value of block will be the return value of dummy_values().
1392
+ 3456
1393
+ end
1394
+ ok {hashobj[:a]} == 1
1395
+ ok {hashobj['b']} == 2
1396
+ ok {hashobj[:c]} == 3
1397
+ not_ok {hashobj}.key?(:x) # because `:x` was not a key
1398
+ ok {ret} == 3456
1399
+ end
1400
+
1401
+ end
1402
+
1403
+ end
1404
+ ```
1405
+
1406
+ * `dummy_values()` is very useful to change envirnment variables temporarily,
1407
+ such as `dummy_values(ENV, 'LANG'=>'en_US.UTF-8')`.
1408
+
1409
+
1410
+ ### `dummy_attrs()`
1411
+
1412
+ `dummy_attrs()` changes object attribute values temporarily.
1413
+
1414
+ test/example35_test.rb:
1415
+
1416
+ ```ruby
1417
+ require 'oktest'
1418
+
1419
+ class User
1420
+ def initialize(id, name)
1421
+ @id = id
1422
+ @name = name
1423
+ end
1424
+ attr_accessor :id, :name
1425
+ end
1426
+
1427
+ Oktest.scope do
1428
+
1429
+ topic "dummy_attrs()" do
1430
+
1431
+ spec "usage #1: without block" do
1432
+ user = User.new(123, "alice")
1433
+ ok {user.id} == 123
1434
+ ok {user.name} == "alice"
1435
+ ret = dummy_attrs(user, :id=>999, :name=>"bob") # !!!!!
1436
+ ok {user.id} == 999
1437
+ ok {user.name} == "bob"
1438
+ ok {ret} == {:id=>999, :name=>"bob"}
1439
+ ## attribute values are recovered at end of spec block.
1440
+ end
1441
+
1442
+ spec "usage #2: with block" do
1443
+ user = User.new(123, "alice")
1444
+ ok {user.id} == 123
1445
+ ok {user.name} == "alice"
1446
+ ret = dummy_attrs(user, :id=>999, :name=>"bob") do |keyvals| # !!!!!
1447
+ ok {user.id} == 999
1448
+ ok {user.name} == "bob"
1449
+ ok {keyvals} == {:id=>999, :name=>"bob"}
1450
+ ## attribute values are recovered at end of this block.
1451
+ ## last value of block will be the return value of dummy_attrs().
1452
+ 4567
1453
+ end
1454
+ ok {user.id} == 123
1455
+ ok {user.name} == "alice"
1456
+ ok {ret} == 4567
1457
+ end
1458
+
1459
+ end
1460
+
1461
+ end
1462
+ ```
1463
+
1464
+
1465
+ ### `dummy_ivars()`
1466
+
1467
+ `dummy_ivars()` changes instance variables in object with dummy values temporarily.
1468
+
1469
+ test/example36_test.rb:
1470
+
1471
+ ```ruby
1472
+ require 'oktest'
1473
+
1474
+ class User
1475
+ def initialize(id, name)
1476
+ @id = id
1477
+ @name = name
1478
+ end
1479
+ attr_reader :id, :name # setter, not accessor
1480
+ end
1481
+
1482
+ Oktest.scope do
1483
+
1484
+ topic "dummy_attrs()" do
1485
+
1486
+ spec "usage #1: without block" do
1487
+ user = User.new(123, "alice")
1488
+ ok {user.id} == 123
1489
+ ok {user.name} == "alice"
1490
+ ret = dummy_ivars(user, :id=>999, :name=>"bob") # !!!!!
1491
+ ok {user.id} == 999
1492
+ ok {user.name} == "bob"
1493
+ ok {ret} == {:id=>999, :name=>"bob"}
1494
+ ## attribute values are recovered at end of spec block.
1495
+ end
1496
+
1497
+ spec "usage #2: with block" do
1498
+ user = User.new(123, "alice")
1499
+ ok {user.id} == 123
1500
+ ok {user.name} == "alice"
1501
+ ret = dummy_ivars(user, :id=>999, :name=>"bob") do |keyvals| # !!!!!
1502
+ ok {user.id} == 999
1503
+ ok {user.name} == "bob"
1504
+ ok {keyvals} == {:id=>999, :name=>"bob"}
1505
+ ## attribute values are recovered at end of this block.
1506
+ ## last value of block will be the return value of dummy_attrs().
1507
+ 6789
1508
+ end
1509
+ ok {user.id} == 123
1510
+ ok {user.name} == "alice"
1511
+ ok {ret} == 6789
1512
+ end
1513
+
1514
+ end
1515
+
1516
+ end
1517
+ ```
1518
+
1519
+
1520
+ ### `recorder()`
1521
+
1522
+ `recorder()` returns Benry::Recorder object.
1523
+ See [Benry::Recorder README](https://github.com/kwatch/benry-ruby/blob/ruby/benry-recorder/README.md)
1524
+ for detals.
1525
+
1526
+ test/example37_test.rb:
1527
+
1528
+ ```ruby
1529
+ require 'oktest'
1530
+
1531
+ class Calc
1532
+ def total(*nums)
1533
+ t = 0; nums.each {|n| t += n } # or: nums.sum()
1534
+ return t
1535
+ end
1536
+ def average(*nums)
1537
+ return total(*nums).to_f / nums.length
1538
+ end
1539
+ end
1540
+
1541
+
1542
+ Oktest.scope do
1543
+
1544
+ topic 'recorder()' do
1545
+
1546
+ spec "records method calls." do
1547
+ ## target object
1548
+ calc = Calc.new
1549
+ ## record method call
1550
+ rec = recorder() # !!!!!
1551
+ rec.record_method(calc, :total)
1552
+ ## method call
1553
+ v = calc.average(1, 2, 3, 4) # calc.average() calls calc.total() internally
1554
+ p v #=> 2.5
1555
+ ## show method call info
1556
+ p rec.length #=> 1
1557
+ p rec[0].obj == calc #=> true
1558
+ p rec[0].name #=> :total
1559
+ p rec[0].args #=> [1, 2, 3, 4]
1560
+ p rec[0].ret #=> 2.5
1561
+ end
1562
+
1563
+ spec "defines fake methods." do
1564
+ ## target object
1565
+ calc = Calc.new
1566
+ ## define fake methods
1567
+ rec = recorder() # !!!!!
1568
+ rec.fake_method(calc, :total=>20, :average=>5.5)
1569
+ ## call fake methods
1570
+ v1 = calc.total(1, 2, 3) # fake method returns dummy value
1571
+ p v1 #=> 20
1572
+ v2 = calc.average(1, 2, 'a'=>3) # fake methods accepts any args
1573
+ p v2 #=> 5.5
1574
+ ## show method call info
1575
+ puts rec.inspect
1576
+ #=> 0: #<Calc:0x00007fdb5482c968>.total(1, 2, 3) #=> 20
1577
+ # 1: #<Calc:0x00007fdb5482c968>.average(1, 2, {"a"=>3}) #=> 5.5
1578
+ end
1579
+
1580
+ end
1581
+
1582
+ end
1583
+ ```
1584
+
1585
+
1586
+
1587
+ ## Tips
1588
+
1589
+
1590
+ ### `ok {}` in MiniTest
1591
+
1592
+ If you want to use `ok {actual} == expected` style assertion in MiniTest,
1593
+ install `minitest-ok` gem instead of `otest` gem.
1594
+
1595
+ test/example41_test.rb:
1596
+
1597
+ ```ruby
1598
+ require 'minitest/spec'
1599
+ require 'minitest/autorun'
1600
+ require 'minitest/ok' # !!!!!
1601
+
1602
+ describe 'MiniTest::Ok' do
1603
+
1604
+ it "helps to write assertions" do
1605
+ ok {1+1} == 2 # !!!!!
1606
+ end
1607
+
1608
+ end
1609
+ ```
1610
+
1611
+ See [minitest-ok README](https://github.com/kwatch/minitest-ok) for details.
1612
+
1613
+
1614
+ ### Testing Rack Application
1615
+
1616
+ `rack-test_app` gem will help you to test Rack application very well.
1617
+
1618
+ test/example42_test.rb:
1619
+
1620
+ ```ruby
1621
+ require 'rack'
1622
+ require 'rack/lint'
1623
+ require 'rack/test_app' # !!!!!
1624
+ require 'oktest'
1625
+
1626
+ app = proc {|env| # sample Rack application
1627
+ text = '{"status":"OK"}'
1628
+ headers = {"Content-Type" => "application/json",
1629
+ "Content-Length" => text.bytesize.to_s}
1630
+ [200, headers, [text]]
1631
+ }
1632
+
1633
+ http = Rack::TestApp.wrap(Rack::Lint.new(app)) # wrap Rack app
1634
+
1635
+ Oktest.scope do
1636
+
1637
+ + topic("GET /api/hello") do
1638
+
1639
+ - spec("returns JSON data.") do
1640
+ response = http.GET("/api/hello") # call Rack app
1641
+ ok {response.status} == 200
1642
+ ok {response.content_type} == "application/json"
1643
+ ok {response.body_json} == {"status"=>"OK"}
1644
+ end
1645
+
1646
+ end
1647
+
1648
+ end
1649
+ ```
1650
+
1651
+ Defining helper method per topic may help you.
1652
+
1653
+ ```ruby
1654
+ $http = http # !!!!
1655
+
1656
+ Oktest.scope do
1657
+
1658
+ + topic("GET /api/hello") do
1659
+
1660
+ def api_call(**kwargs) # !!!!!
1661
+ $http.GET("/api/hello", **kwargs) # !!!!!
1662
+ end # !!!!!
1663
+
1664
+ - spec("returns JSON data.") do
1665
+ response = api_call() # !!!!!
1666
+ ok {response.status} == 200
1667
+ ok {response.content_type} == "application/json"
1668
+ ok {response.body_json} == {"status"=>"OK"}
1669
+ end
1670
+
1671
+ end
1672
+
1673
+ + topic("POST /api/hello") do
1674
+
1675
+ def api_call(**kwargs) # !!!!!
1676
+ $http.POST("/api/hello", **kwargs) # !!!!!
1677
+ end # !!!!!
1678
+
1679
+ ....
1680
+
1681
+ end
1682
+
1683
+ end
1684
+ ```
1685
+
1686
+
1687
+ ### Traverser Class
1688
+
1689
+ Oktest.rb provides `Traverser` class which implements Visitor pattern.
1690
+
1691
+ test/example44_test.rb:
1692
+
1693
+ ```ruby
1694
+ require 'oktest'
1695
+
1696
+ Oktest.scope do
1697
+ + topic('Example Topic') do
1698
+ - spec("sample #1") do ok {1+1} == 2 end
1699
+ - spec("sample #2") do ok {1-1} == 0 end
1700
+ + case_when('some condition...') do
1701
+ - spec("sample #3") do ok {1*1} == 1 end
1702
+ end
1703
+ + case_else() do
1704
+ - spec("sample #4") do ok {1/1} == 1 end
1705
+ end
1706
+ end
1707
+ end
1708
+
1709
+ ## custom traverser class
1710
+ class MyTraverser < Oktest::Traverser # !!!!!
1711
+ def on_scope(filename, tag, depth) # !!!!!
1712
+ print " " * depth
1713
+ print "# scope: #{filename}"
1714
+ print " (tag: #{tag})" if tag
1715
+ print "\n"
1716
+ yield # should yield !!!
1717
+ end
1718
+ def on_topic(target, tag, depth) # !!!!!
1719
+ print " " * depth
1720
+ print "+ topic: #{target}"
1721
+ print " (tag: #{tag})" if tag
1722
+ print "\n"
1723
+ yield # should yield !!!
1724
+ end
1725
+ def on_case(cond, tag, depth) # !!!!!
1726
+ print " " * depth
1727
+ print "+ case: #{cond}"
1728
+ print " (tag: #{tag})" if tag
1729
+ print "\n"
1730
+ yield # should yield !!!
1731
+ end
1732
+ def on_spec(desc, tag, depth) # !!!!!
1733
+ print " " * depth
1734
+ print "- spec: #{desc}"
1735
+ print " (tag: #{tag})" if tag
1736
+ print "\n"
1737
+ end
1738
+ end
1739
+
1740
+ ## run custom traverser
1741
+ Oktest::Config.auto_run = false # stop running test cases
1742
+ MyTraverser.new.start()
1743
+ ```
1744
+
1745
+ Result:
1746
+
1747
+ ```terminal
1748
+ $ ruby test/example44_test.rb
1749
+ # scope: test/example44_test.rb
1750
+ + topic: Example Topic
1751
+ - spec: sample #1
1752
+ - spec: sample #2
1753
+ + case: When some condition...
1754
+ - spec: sample #3
1755
+ + case: Else
1756
+ - spec: sample #4
1757
+ ```
1758
+
1759
+
1760
+ ### Benchmarks
1761
+
1762
+ Oktest.rb gem file contains benchmark script.
1763
+ It shows that Oktest.rb runs more than three times faster than RSpec.
1764
+
1765
+ ```terminal
1766
+ $ gem install oktest # ver 1.0.0
1767
+ $ gem install rspec # ver 3.10.0
1768
+ $ gem install minitest # ver 5.14.4
1769
+ $ gem install test-unit # ver 3.4.4
1770
+
1771
+ $ cp -pr $GEM_HOME/gems/oktest-1.0.0/benchmark .
1772
+ $ cd benchmark/
1773
+ $ rake -T
1774
+ $ ruby --version
1775
+ ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin18]
1776
+
1777
+ $ rake benchmark:all
1778
+ ```
1779
+
1780
+ Example result:
1781
+
1782
+ ```
1783
+ ==================== oktest ====================
1784
+ oktest -sq run_all.rb
1785
+
1786
+ ## total:100000 (pass:100000, fail:0, error:0, skip:0, todo:0) in 4.86s
1787
+
1788
+ 8.536 real 8.154 user 0.245 sys
1789
+
1790
+ ==================== oktest:faster ====================
1791
+ oktest -sq --faster run_all.rb
1792
+
1793
+ ## total:100000 (pass:100000, fail:0, error:0, skip:0, todo:0) in 1.64s
1794
+
1795
+ 5.068 real 4.819 user 0.202 sys
1796
+
1797
+ ==================== rspec ====================
1798
+ rspec run_all.rb | tail -4
1799
+
1800
+ Finished in 14.44 seconds (files took 15.81 seconds to load)
1801
+ 100000 examples, 0 failures
1802
+
1803
+
1804
+ 30.798 real 26.565 user 4.392 sys
1805
+
1806
+ ==================== minitest ====================
1807
+ ruby run_all.rb | tail -4
1808
+
1809
+ Finished in 5.190405s, 19266.3193 runs/s, 19266.3193 assertions/s.
1810
+
1811
+ 100000 runs, 100000 assertions, 0 failures, 0 errors, 0 skips
1812
+
1813
+ 8.767 real 8.157 user 0.761 sys
1814
+
1815
+ ==================== testunit ====================
1816
+ ruby run_all.rb | tail -5
1817
+ -------------------------------------------------------------------------------
1818
+ 100000 tests, 100000 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
1819
+ 100% passed
1820
+ -------------------------------------------------------------------------------
1821
+ 8957.19 tests/s, 8957.19 assertions/s
1822
+
1823
+ 17.838 real 17.201 user 0.879 sys
1824
+ ```
1825
+
1826
+
1827
+ ### `--faster` Option
1828
+
1829
+ `ok {}` is slightly slower than `assert()` in MiniTest.
1830
+ In almost case, you don't need to care about it. But if you are working in
1831
+ very larget project and you want to run test scripts as fast as possible,
1832
+ try `--faster` option of `oktest` command.
1833
+
1834
+ ```terminal
1835
+ $ oktest -s quiet --faster test/ ## only for very large project
1836
+ ```
1837
+
1838
+ Or set `Oktest::Config.ok_location = false` in your test script.
1839
+
1840
+ ```ruby
1841
+ require 'oktest'
1842
+ Oktest::Config.ok_location = false ## only for very large project
1843
+ ```
1844
+
1845
+
1846
+
1847
+ ## Change Log
1848
+
1849
+ See [CHANGES.md](https://github.com/kwatch/oktest/blob/ruby/ruby/CHANGES.md).
1850
+
1851
+
1852
+
1853
+ ## License and Copyright
1854
+
1855
+ * $License: MIT License $
1856
+ * $Copyright: copyright(c) 2011-2021 kuwata-lab.com all rights reserved $