oktest 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +21 -0
- data/README.md +1856 -0
- data/Rakefile.rb +61 -0
- data/benchmark/Rakefile.rb +230 -0
- data/benchmark/run_all.rb +6 -0
- data/bin/oktest +3 -0
- data/lib/oktest.rb +2359 -0
- data/oktest.gemspec +45 -0
- data/test/assertion_test.rb +817 -0
- data/test/filter_test.rb +379 -0
- data/test/fixture_test.rb +205 -0
- data/test/generator_test.rb +123 -0
- data/test/helper_test.rb +542 -0
- data/test/initialize.rb +14 -0
- data/test/mainapp_test.rb +625 -0
- data/test/misc_test.rb +80 -0
- data/test/node_test.rb +669 -0
- data/test/reporter_test.rb +547 -0
- data/test/run_all.rb +13 -0
- data/test/runner_test.rb +544 -0
- data/test/tc.rb +115 -0
- data/test/util_test.rb +258 -0
- data/test/visitor_test.rb +292 -0
- metadata +105 -0
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 $
|