tokyo 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +335 -0
- data/Rakefile +11 -0
- data/bin/tokyo +4 -0
- data/lib/tokyo/assert.rb +148 -0
- data/lib/tokyo/core_ext.rb +103 -0
- data/lib/tokyo/expectations/raise.rb +32 -0
- data/lib/tokyo/expectations/return.rb +32 -0
- data/lib/tokyo/expectations/throw.rb +24 -0
- data/lib/tokyo/expectations/with.rb +36 -0
- data/lib/tokyo/expectations.rb +103 -0
- data/lib/tokyo/pretty_print.rb +55 -0
- data/lib/tokyo/run.rb +126 -0
- data/lib/tokyo/unit.rb +185 -0
- data/lib/tokyo/util/assert_raise.rb +54 -0
- data/lib/tokyo/util/assert_throw.rb +40 -0
- data/lib/tokyo/util/refute_raise.rb +48 -0
- data/lib/tokyo/util/refute_throw.rb +34 -0
- data/lib/tokyo/util.rb +91 -0
- data/lib/tokyo.rb +121 -0
- data/test/assert_test.rb +98 -0
- data/test/context_inheritance_test.rb +52 -0
- data/test/hooks_test.rb +35 -0
- data/test/raise_test.rb +66 -0
- data/test/receive_and_raise_test.rb +83 -0
- data/test/receive_and_return_test.rb +46 -0
- data/test/receive_and_throw_test.rb +74 -0
- data/test/receive_test.rb +71 -0
- data/test/receive_with_test.rb +57 -0
- data/test/refute_raise_test.rb +90 -0
- data/test/refute_throw_test.rb +42 -0
- data/test/setup.rb +25 -0
- data/test/skip_test.rb +35 -0
- data/test/throw_test.rb +58 -0
- data/tokyo.gemspec +27 -0
- metadata +195 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Tokyo
|
2
|
+
class Expectation
|
3
|
+
|
4
|
+
# ensure received message returns expected value
|
5
|
+
#
|
6
|
+
# @note if block given it will have precedence over arguments
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# n = mock(1)
|
10
|
+
# expect(n).to_receive(:+).with(1).and_return(2)
|
11
|
+
#
|
12
|
+
def and_return value = nil, &block
|
13
|
+
@return = block || value
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def assert_message_returned_correct_value
|
18
|
+
return unless @return
|
19
|
+
if @return.is_a?(Proc)
|
20
|
+
received_messages.find {|log| @return.call(log[:returned])} || Tokyo.fail([
|
21
|
+
'Looks like :%s message never returned expected value' % expected_message,
|
22
|
+
'See validation block'
|
23
|
+
], @caller)
|
24
|
+
else
|
25
|
+
received_messages.find {|log| log[:returned] == @return} || Tokyo.fail([
|
26
|
+
'Looks like :%s message never returned expected value:' % expected_message,
|
27
|
+
Array(@return).map {|x| Tokyo.pp(x)}.join(', ')
|
28
|
+
], @caller)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Tokyo
|
2
|
+
class Expectation
|
3
|
+
|
4
|
+
# ensure received message throws as expected
|
5
|
+
#
|
6
|
+
# @note if block given it will have precedence over arguments
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# x = mock(X.new)
|
10
|
+
# expect(x).to_receive(:y).and_throw(:z)
|
11
|
+
#
|
12
|
+
def and_throw symbol = nil, &block
|
13
|
+
@throw = [symbol, block]
|
14
|
+
end
|
15
|
+
|
16
|
+
def assert_message_thrown_as_expected
|
17
|
+
return unless @throw
|
18
|
+
received_messages.each do |log|
|
19
|
+
next unless f = Tokyo.assert_thrown_as_expected(log, *@throw)
|
20
|
+
Tokyo.fail(f, log[:caller])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Tokyo
|
2
|
+
class Expectation
|
3
|
+
|
4
|
+
# ensure expected message received with correct arguments
|
5
|
+
#
|
6
|
+
# @note if block given it will have precedence over arguments
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# test :some_test do
|
10
|
+
# some_object = mock(SomeObject.new)
|
11
|
+
# expect(some_object).to_receive(:some_method).with(:some, :args)
|
12
|
+
# # call `some_object.some_method(:some, :args)` for test to pass
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def with *args, &block
|
16
|
+
@with = block || args
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def assert_message_received_with_correct_arguments
|
22
|
+
return unless @with
|
23
|
+
if @with.is_a?(Proc)
|
24
|
+
received_messages.find {|log| @with.call(log[:arguments])} || Tokyo.fail([
|
25
|
+
'Looks like :%s message never was called with expected arguments' % expected_message,
|
26
|
+
'See validation block'
|
27
|
+
], @caller)
|
28
|
+
else
|
29
|
+
received_messages.find {|log| log[:arguments] == @with} || Tokyo.fail([
|
30
|
+
'Looks like :%s message never was called with expected arguments:' % expected_message,
|
31
|
+
Array(@with).map {|x| Tokyo.pp(x)}.join(', ')
|
32
|
+
], @caller)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Tokyo
|
2
|
+
class Expectation
|
3
|
+
|
4
|
+
def self.restore_object_status obj
|
5
|
+
return unless obj.instance_variable_get(:@__tokyo__original_methods__)
|
6
|
+
|
7
|
+
obj.instance_variable_get(:@__tokyo__original_methods__).each_pair do |n,m|
|
8
|
+
obj.define_singleton_method(n, &m)
|
9
|
+
end
|
10
|
+
|
11
|
+
obj.instance_variable_set(:@__tokyo__original_methods__, {})
|
12
|
+
obj.instance_variable_set(:@__tokyo__received_messages__, {})
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :expected_message, :received_messages
|
16
|
+
|
17
|
+
def initialize object, expected_message, assert, caller
|
18
|
+
@object = object
|
19
|
+
@expected_message = expected_message.to_sym
|
20
|
+
@assert = assert
|
21
|
+
@caller = caller
|
22
|
+
proxify(@object, @expected_message)
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate
|
26
|
+
@received_messages = @object.__tokyo__received_messages__[expected_message] || []
|
27
|
+
return refute_message_received unless @assert
|
28
|
+
assert_message_received
|
29
|
+
assert_message_received_with_correct_arguments
|
30
|
+
assert_message_returned_correct_value
|
31
|
+
assert_message_raised_as_expected
|
32
|
+
assert_message_thrown_as_expected
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def proxify object, method
|
37
|
+
mount_original_methods_depot(object)
|
38
|
+
mount_received_messages_depot(object)
|
39
|
+
store_original_method(object, method)
|
40
|
+
|
41
|
+
object.define_singleton_method method do |*a,&b|
|
42
|
+
log = {arguments: a, block: b, caller: Tokyo.relative_location(caller[0])}
|
43
|
+
(__tokyo__received_messages__[method] ||= []).push(log)
|
44
|
+
|
45
|
+
if __tokyo__original_methods__[method]
|
46
|
+
return begin
|
47
|
+
log[:returned] = __tokyo__original_methods__[method].call(*a, &b)
|
48
|
+
rescue UncaughtThrowError => e
|
49
|
+
log[:thrown] = Tokyo.extract_thrown_symbol(e)
|
50
|
+
rescue Exception => e
|
51
|
+
log[:raised] = e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if respond_to?(:method_missing)
|
56
|
+
return begin
|
57
|
+
log[:returned] = __send__(:method_missing, method, *a, &b)
|
58
|
+
rescue UncaughtThrowError => e
|
59
|
+
log[:thrown] = Tokyo.extract_thrown_symbol(e)
|
60
|
+
rescue Exception => e
|
61
|
+
log[:raised] = e
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
log[:raised] = NoMethodError.new("undefined method `%s' for %s:%s" % [method, self.inspect, self.class])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def mount_original_methods_depot object
|
70
|
+
return if object.respond_to?(:__tokyo__original_methods__)
|
71
|
+
def object.__tokyo__original_methods__; @__tokyo__original_methods__ ||= {} end
|
72
|
+
end
|
73
|
+
|
74
|
+
def mount_received_messages_depot object
|
75
|
+
return if object.respond_to?(:__tokyo__received_messages__)
|
76
|
+
def object.__tokyo__received_messages__; @__tokyo__received_messages__ ||= {} end
|
77
|
+
end
|
78
|
+
|
79
|
+
def store_original_method object, method
|
80
|
+
return unless object.respond_to?(method)
|
81
|
+
object.__tokyo__original_methods__[method] ||= object.method(method)
|
82
|
+
end
|
83
|
+
|
84
|
+
def assert_message_received
|
85
|
+
Tokyo.fail('Expected %s to receive %s message' % [
|
86
|
+
Tokyo.pp(@object),
|
87
|
+
Tokyo.pp(expected_message)
|
88
|
+
], @caller) if received_messages.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
def refute_message_received
|
92
|
+
Tokyo.fail('Not Expected %s to receive %s message' % [
|
93
|
+
Tokyo.pp(@object),
|
94
|
+
Tokyo.pp(expected_message)
|
95
|
+
], @caller) if received_messages.any?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
require 'tokyo/expectations/with'
|
101
|
+
require 'tokyo/expectations/return'
|
102
|
+
require 'tokyo/expectations/raise'
|
103
|
+
require 'tokyo/expectations/throw'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'coderay'
|
3
|
+
|
4
|
+
# Stolen from [Pry](https://github.com/pry/pry)
|
5
|
+
#
|
6
|
+
# Copyright (c) 2013 John Mair (banisterfiend)
|
7
|
+
# Copyright (c) 2015 Slee Woo (sleewoo)
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# 'Software'), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
module Tokyo
|
29
|
+
def pp obj
|
30
|
+
out = ''
|
31
|
+
q = Tokyo::PrettyPrint.new(out)
|
32
|
+
q.guard_inspect_key { q.pp(obj) }
|
33
|
+
q.flush
|
34
|
+
out
|
35
|
+
end
|
36
|
+
|
37
|
+
class PrettyPrint < ::PP
|
38
|
+
OBJECT_LITERAL_FORMAT = "\e[32m%s\e[0m".freeze
|
39
|
+
|
40
|
+
def text str, width = str.length
|
41
|
+
if str.include?("\e[")
|
42
|
+
super "%s\e[0m" % str, width
|
43
|
+
elsif str.start_with?('#<') || str == '=' || str == '>'
|
44
|
+
super highlight_object_literal(str), width
|
45
|
+
else
|
46
|
+
super CodeRay.scan(str, :ruby).term, width
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def highlight_object_literal object_literal
|
52
|
+
OBJECT_LITERAL_FORMAT % object_literal
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/tokyo/run.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
module Tokyo
|
2
|
+
|
3
|
+
def progress
|
4
|
+
@progress ||= TTY::ProgressBar.new ':current of :total [:bar]' do |cfg|
|
5
|
+
cfg.total = units.map {|u| u.tests.size}.reduce(:+) || 0
|
6
|
+
cfg.width = TTY::Screen.width
|
7
|
+
cfg.complete = '.'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def run pattern_or_files = DEFAULT_PATTERN
|
12
|
+
specs = 0
|
13
|
+
tests = 0
|
14
|
+
assertions = 0
|
15
|
+
find_files(pattern_or_files).shuffle.each do |file|
|
16
|
+
r, w = IO.pipe
|
17
|
+
pid = Kernel.fork do
|
18
|
+
r.close
|
19
|
+
load_file(file)
|
20
|
+
progress.log ''
|
21
|
+
progress.log cyan(relative_location(file))
|
22
|
+
units.shuffle.each do |unit|
|
23
|
+
# exceptions raised inside unit#__run__ will be treated as failures and pretty printed.
|
24
|
+
# any other exceptions will be treated as implementation errors and ugly printed.
|
25
|
+
unit.tests.keys.shuffle.each do |test|
|
26
|
+
status = unit.run(test)
|
27
|
+
if status.is_a?(Skip)
|
28
|
+
w.puts({skip: true, reason: status.reason, caller: status.caller}.to_json)
|
29
|
+
else
|
30
|
+
unless status == :__tokyo_passed__
|
31
|
+
render_failure(unit, unit.tests[test], status)
|
32
|
+
Kernel.exit(1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
progress.advance
|
36
|
+
end
|
37
|
+
end
|
38
|
+
w.puts(totals.to_json)
|
39
|
+
end
|
40
|
+
_, status = Process.waitpid2(pid)
|
41
|
+
Kernel.exit(1) unless status.success?
|
42
|
+
w.close
|
43
|
+
while line = r.gets
|
44
|
+
line = JSON.parse(line)
|
45
|
+
if line['skip']
|
46
|
+
skips << line
|
47
|
+
elsif line['totals']
|
48
|
+
specs += line['specs']
|
49
|
+
tests += line['tests']
|
50
|
+
assertions += line['assertions']
|
51
|
+
else
|
52
|
+
raise('Incomprehensible message received: %s' % line)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
render_skips
|
57
|
+
render_totals(specs, tests, assertions)
|
58
|
+
end
|
59
|
+
|
60
|
+
def totals
|
61
|
+
{
|
62
|
+
totals: true,
|
63
|
+
specs: units.select {|u| u.__ancestors__.empty?}.size,
|
64
|
+
tests: units.map {|u| u.tests.size}.reduce(:+),
|
65
|
+
assertions: total_assertions.size
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def render_totals specs, tests, assertions
|
70
|
+
puts
|
71
|
+
puts
|
72
|
+
puts bold.cyan(' Specs: %i' % specs)
|
73
|
+
puts bold.cyan(' Tests: %i' % tests)
|
74
|
+
puts bold.cyan(' Assertions: %i' % assertions)
|
75
|
+
puts
|
76
|
+
end
|
77
|
+
|
78
|
+
def render_failure unit, test_uuid, failure
|
79
|
+
indent = ''
|
80
|
+
[*unit.__ancestors__, unit].each do |u|
|
81
|
+
progress.log indent + u.__identity__
|
82
|
+
indent << INDENT
|
83
|
+
end
|
84
|
+
progress.log indent + test_uuid
|
85
|
+
indent << INDENT
|
86
|
+
case failure
|
87
|
+
when Exception
|
88
|
+
render_exception(indent, failure)
|
89
|
+
when GenericFailure, AssertionFailure
|
90
|
+
render_caller(indent, failure.caller)
|
91
|
+
__send__('render_%s' % failure.class.name.split('::').last, indent, failure)
|
92
|
+
else
|
93
|
+
progress.log(indent + failure.inspect)
|
94
|
+
end
|
95
|
+
progress.log ''
|
96
|
+
end
|
97
|
+
|
98
|
+
def render_exception indent, failure
|
99
|
+
progress.log indent + underline.bright_red([failure.class, failure.message]*': ')
|
100
|
+
pretty_backtrace(failure).each {|l| progress.log(indent + l)}
|
101
|
+
end
|
102
|
+
|
103
|
+
def render_GenericFailure indent, failure
|
104
|
+
Array(failure.reason).each {|l| progress.log(indent + l.to_s)}
|
105
|
+
end
|
106
|
+
|
107
|
+
def render_AssertionFailure indent, failure
|
108
|
+
progress.log indent + cyan('a: ') + pp(failure.object)
|
109
|
+
progress.log indent + cyan('b: ') + failure.arguments.map {|a| pp(a)}.join(', ')
|
110
|
+
end
|
111
|
+
|
112
|
+
def render_caller indent, caller
|
113
|
+
return unless caller
|
114
|
+
progress.log indent + underline.bright_red(readline(caller))
|
115
|
+
end
|
116
|
+
|
117
|
+
def render_skips
|
118
|
+
return if skips.empty?
|
119
|
+
puts
|
120
|
+
puts bold.magenta('Skips:')
|
121
|
+
skips.each do |skip|
|
122
|
+
puts ' %s (%s)' % [blue(skip['reason'] || 'skip'), relative_location(skip['caller'])]
|
123
|
+
end
|
124
|
+
puts
|
125
|
+
end
|
126
|
+
end
|
data/lib/tokyo/unit.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
module Tokyo
|
2
|
+
class Unit
|
3
|
+
|
4
|
+
# @example
|
5
|
+
# assert(x) == y
|
6
|
+
# assert(x).include?(y)
|
7
|
+
# assert(x).nil?
|
8
|
+
# refute(a).include?(b)
|
9
|
+
# fail_if(a).include?(b)
|
10
|
+
[
|
11
|
+
:assert,
|
12
|
+
:refute
|
13
|
+
].each do |meth|
|
14
|
+
define_method meth do |obj = nil, &block|
|
15
|
+
__objects__[obj = block ? Tokyo.call_block(block) : {returned: obj}.freeze] = true
|
16
|
+
__assertions__.push(Tokyo::Assert.new(obj, meth, caller[0])).last
|
17
|
+
end
|
18
|
+
end
|
19
|
+
alias expect assert
|
20
|
+
alias fail_if refute
|
21
|
+
|
22
|
+
# stop executing current test and mark it as skipped
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# test :something do
|
26
|
+
# skip "recheck this after fixing X"
|
27
|
+
# assert(x) == y # this wont run
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
def skip reason = nil
|
31
|
+
throw(:__tokyo_status__, Tokyo::Skip.new(reason, caller[0]))
|
32
|
+
end
|
33
|
+
|
34
|
+
def __run__ test, before, around, after
|
35
|
+
__send__(before) if before
|
36
|
+
if around
|
37
|
+
__send__(around, proc {__send__(test)})
|
38
|
+
else
|
39
|
+
__send__(test)
|
40
|
+
end
|
41
|
+
__send__(after) if after
|
42
|
+
__assertions__.each(&:__validate_expectations__)
|
43
|
+
__objects__.each_key {|o| Tokyo::Expectation.restore_object_status(o[:returned])}
|
44
|
+
:__tokyo_passed__
|
45
|
+
rescue Exception => e
|
46
|
+
throw(:__tokyo_status__, e)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def __objects__
|
51
|
+
@__objects__ ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def __assertions__
|
55
|
+
@__assertions__ ||= []
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class << Unit
|
60
|
+
|
61
|
+
def inherited base
|
62
|
+
base.instance_variable_set(:@__tokyo_hooks__, Marshal.load(Marshal.dump(hooks)))
|
63
|
+
end
|
64
|
+
|
65
|
+
def spec
|
66
|
+
raise(NotImplementedError, 'Nested specs not supported. Please use a context instead')
|
67
|
+
end
|
68
|
+
|
69
|
+
# define a context inside current spec/context.
|
70
|
+
#
|
71
|
+
# @param label
|
72
|
+
# @param &block
|
73
|
+
#
|
74
|
+
def context label, &block
|
75
|
+
return unless block
|
76
|
+
Tokyo.define_and_register_a_context(label, block, self)
|
77
|
+
end
|
78
|
+
|
79
|
+
# define a test
|
80
|
+
#
|
81
|
+
# @param label
|
82
|
+
# @param &block
|
83
|
+
#
|
84
|
+
def test label, &block
|
85
|
+
return unless block
|
86
|
+
tests[label] = Tokyo.identity_string(:test, label, block)
|
87
|
+
define_method(tests[label], &block)
|
88
|
+
end
|
89
|
+
alias it test
|
90
|
+
alias should test
|
91
|
+
|
92
|
+
def tests
|
93
|
+
@__tokyo_tests__ ||= {}
|
94
|
+
end
|
95
|
+
|
96
|
+
# run some code before/around/after tests
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# spec User do
|
100
|
+
# before do
|
101
|
+
# @users = ...
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# test :login do
|
105
|
+
# # @users available here
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# spec User do
|
111
|
+
# around |&test|
|
112
|
+
# SomeApi.with_auth do
|
113
|
+
# test.call # run the test
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# test :authorize do
|
118
|
+
# # will run inside `with_auth` block
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# @note hooks are inherited from parent spec or context
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# spec Math do
|
126
|
+
# before { @n = rand }
|
127
|
+
#
|
128
|
+
# context :PI do
|
129
|
+
# test :some_test do
|
130
|
+
# # @n available here
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# @note defined hooks will override inherited ones
|
136
|
+
#
|
137
|
+
# @example
|
138
|
+
# spec Math do
|
139
|
+
# before { @n = 1 }
|
140
|
+
#
|
141
|
+
# context :x do
|
142
|
+
# before { @n = 0 }
|
143
|
+
# test :some_test do
|
144
|
+
# # @n is zero here
|
145
|
+
# end
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
[
|
150
|
+
:before,
|
151
|
+
:around,
|
152
|
+
:after
|
153
|
+
].each do |hook|
|
154
|
+
define_method hook do |&block|
|
155
|
+
block || raise(ArgumentError, 'block missing')
|
156
|
+
hooks[hook] = :"__tokyo__#{hook}_hook__"
|
157
|
+
define_method(hooks[hook], &block)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def hooks
|
162
|
+
@__tokyo_hooks__ ||= {}
|
163
|
+
end
|
164
|
+
|
165
|
+
# skipping a whole spec/context
|
166
|
+
#
|
167
|
+
# @example
|
168
|
+
# spec Array do
|
169
|
+
# skip
|
170
|
+
#
|
171
|
+
# # code here wont be executed
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
def skip reason = nil
|
175
|
+
throw(:__tokyo_skip__, Tokyo::Skip.new(reason, caller[0]))
|
176
|
+
end
|
177
|
+
|
178
|
+
def run test
|
179
|
+
tests[test] || raise(StandardError, 'Undefined test %s at "%s"' % [test.inspect, __identity__])
|
180
|
+
catch :__tokyo_status__ do
|
181
|
+
allocate.__run__(tests[test], hooks[:before], hooks[:around], hooks[:after])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Tokyo
|
2
|
+
|
3
|
+
def assert_raised_as_expected object, expected_type = nil, expected_message = nil, block = nil
|
4
|
+
f = assert_raised(object)
|
5
|
+
return f if f
|
6
|
+
|
7
|
+
return assert_raised_as_expected_by_block(object, block) if block
|
8
|
+
|
9
|
+
if expected_type
|
10
|
+
f = assert_raised_expected_type(object, expected_type)
|
11
|
+
return f if f
|
12
|
+
end
|
13
|
+
|
14
|
+
if expected_message
|
15
|
+
f = assert_raised_expected_message(object, expected_message)
|
16
|
+
return f if f
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def assert_raised object
|
22
|
+
return [
|
23
|
+
'Expected a exception to be raised at %s' % object[:caller]
|
24
|
+
] unless object[:raised]
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_raised_as_expected_by_block object, block
|
29
|
+
return [
|
30
|
+
'Looks like wrong or no error raised at %s' % object[:caller],
|
31
|
+
'See validation block'
|
32
|
+
] unless block.call(object[:raised])
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_raised_expected_type object, expected_type
|
37
|
+
return [
|
38
|
+
'Expected a %s to be raised at %s' % [expected_type, object[:caller]],
|
39
|
+
'Instead a %s raised' % object[:raised].class
|
40
|
+
] unless object[:raised].class == expected_type
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def assert_raised_expected_message object, expected_message
|
45
|
+
regexp = expected_message.is_a?(Regexp) ? expected_message : /\A#{expected_message}\z/
|
46
|
+
return [
|
47
|
+
'Expected the exception raised at %s' % object[:caller],
|
48
|
+
'to match "%s"' % regexp.source,
|
49
|
+
'Instead it looks like',
|
50
|
+
pp(object[:raised].message)
|
51
|
+
] unless object[:raised].message =~ regexp
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Tokyo
|
2
|
+
|
3
|
+
def assert_thrown_as_expected object, expected_symbol = nil, block = nil
|
4
|
+
f = assert_thrown(object)
|
5
|
+
return f if f
|
6
|
+
|
7
|
+
return assert_thrown_as_expected_by_block(object, block) if block
|
8
|
+
|
9
|
+
if expected_symbol
|
10
|
+
f = assert_expected_symbol_thrown(object, expected_symbol)
|
11
|
+
return f if f
|
12
|
+
end
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def assert_thrown_as_expected_by_block object, block
|
17
|
+
return [
|
18
|
+
'Looks like wrong or no symbol thrown at %s' % object[:caller],
|
19
|
+
'See validating block'
|
20
|
+
] unless block.call(object[:thrown])
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_thrown object
|
25
|
+
return [
|
26
|
+
'Expected a symbol to be thrown at %s' % object[:caller]
|
27
|
+
] unless object[:thrown]
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_expected_symbol_thrown object, expected_symbol
|
32
|
+
return begin
|
33
|
+
[
|
34
|
+
'Expected :%s to be thrown at %s' % [expected_symbol, object[:caller]],
|
35
|
+
'Instead :%s thrown' % object[:thrown]
|
36
|
+
]
|
37
|
+
end unless expected_symbol == object[:thrown]
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Tokyo
|
2
|
+
|
3
|
+
def refute_raised_as_expected object, expected_type, expected_message, block = nil
|
4
|
+
f = refute_raised(object, expected_type || expected_message)
|
5
|
+
return f if f
|
6
|
+
|
7
|
+
if expected_type
|
8
|
+
f = refute_raised_expected_type(object, expected_type)
|
9
|
+
return f if f
|
10
|
+
end
|
11
|
+
|
12
|
+
if expected_message
|
13
|
+
f = refute_raised_expected_message(object, expected_message)
|
14
|
+
return f if f
|
15
|
+
end
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def refute_raised object, should_raise = false
|
20
|
+
if should_raise
|
21
|
+
return [
|
22
|
+
'Expected a exception to be raised at %s' % object[:caller]
|
23
|
+
] unless object[:raised]
|
24
|
+
else
|
25
|
+
return [
|
26
|
+
'A unexpected exception raised at %s' % object[:caller],
|
27
|
+
object[:raised]
|
28
|
+
] if object[:raised]
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def refute_raised_expected_type object, expected_type
|
34
|
+
return [
|
35
|
+
'Not expected a %s to be raised at %s' % [object[:raised].class, object[:caller]],
|
36
|
+
] if object[:raised].class == expected_type
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def refute_raised_expected_message object, expected_message
|
41
|
+
regexp = expected_message.is_a?(Regexp) ? expected_message : /\A#{expected_message}\z/
|
42
|
+
return [
|
43
|
+
'Not expected raised exception to match %s' % regexp.source,
|
44
|
+
object[:raised]
|
45
|
+
] if object[:raised].message =~ regexp
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|