minispec 0.0.1
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/.pryrc +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +2140 -0
- data/Rakefile +11 -0
- data/bin/minispec +4 -0
- data/lib/minispec.rb +175 -0
- data/lib/minispec/api.rb +2 -0
- data/lib/minispec/api/class.rb +195 -0
- data/lib/minispec/api/class/after.rb +49 -0
- data/lib/minispec/api/class/around.rb +54 -0
- data/lib/minispec/api/class/before.rb +101 -0
- data/lib/minispec/api/class/helpers.rb +116 -0
- data/lib/minispec/api/class/let.rb +44 -0
- data/lib/minispec/api/class/tests.rb +33 -0
- data/lib/minispec/api/instance.rb +158 -0
- data/lib/minispec/api/instance/mocks/doubles.rb +36 -0
- data/lib/minispec/api/instance/mocks/mocks.rb +319 -0
- data/lib/minispec/api/instance/mocks/spies.rb +17 -0
- data/lib/minispec/api/instance/mocks/stubs.rb +105 -0
- data/lib/minispec/helpers.rb +1 -0
- data/lib/minispec/helpers/array.rb +56 -0
- data/lib/minispec/helpers/booleans.rb +108 -0
- data/lib/minispec/helpers/generic.rb +24 -0
- data/lib/minispec/helpers/mocks/expectations.rb +29 -0
- data/lib/minispec/helpers/mocks/spies.rb +36 -0
- data/lib/minispec/helpers/raise.rb +44 -0
- data/lib/minispec/helpers/throw.rb +29 -0
- data/lib/minispec/mocks.rb +11 -0
- data/lib/minispec/mocks/expectations.rb +77 -0
- data/lib/minispec/mocks/stubs.rb +178 -0
- data/lib/minispec/mocks/validations.rb +80 -0
- data/lib/minispec/mocks/validations/amount.rb +63 -0
- data/lib/minispec/mocks/validations/arguments.rb +161 -0
- data/lib/minispec/mocks/validations/caller.rb +43 -0
- data/lib/minispec/mocks/validations/order.rb +47 -0
- data/lib/minispec/mocks/validations/raise.rb +111 -0
- data/lib/minispec/mocks/validations/return.rb +74 -0
- data/lib/minispec/mocks/validations/throw.rb +91 -0
- data/lib/minispec/mocks/validations/yield.rb +141 -0
- data/lib/minispec/proxy.rb +201 -0
- data/lib/minispec/reporter.rb +185 -0
- data/lib/minispec/utils.rb +139 -0
- data/lib/minispec/utils/differ.rb +325 -0
- data/lib/minispec/utils/pretty_print.rb +51 -0
- data/lib/minispec/utils/raise.rb +123 -0
- data/lib/minispec/utils/throw.rb +140 -0
- data/minispec.gemspec +27 -0
- data/test/mocks/expectations/amount.rb +67 -0
- data/test/mocks/expectations/arguments.rb +126 -0
- data/test/mocks/expectations/caller.rb +55 -0
- data/test/mocks/expectations/generic.rb +35 -0
- data/test/mocks/expectations/order.rb +46 -0
- data/test/mocks/expectations/raise.rb +166 -0
- data/test/mocks/expectations/return.rb +71 -0
- data/test/mocks/expectations/throw.rb +113 -0
- data/test/mocks/expectations/yield.rb +109 -0
- data/test/mocks/spies/amount.rb +68 -0
- data/test/mocks/spies/arguments.rb +57 -0
- data/test/mocks/spies/generic.rb +61 -0
- data/test/mocks/spies/order.rb +38 -0
- data/test/mocks/spies/raise.rb +158 -0
- data/test/mocks/spies/return.rb +71 -0
- data/test/mocks/spies/throw.rb +113 -0
- data/test/mocks/spies/yield.rb +101 -0
- data/test/mocks/test__doubles.rb +98 -0
- data/test/mocks/test__expectations.rb +27 -0
- data/test/mocks/test__mocks.rb +197 -0
- data/test/mocks/test__proxies.rb +61 -0
- data/test/mocks/test__spies.rb +43 -0
- data/test/mocks/test__stubs.rb +427 -0
- data/test/proxified_asserts.rb +34 -0
- data/test/setup.rb +53 -0
- data/test/test__around.rb +58 -0
- data/test/test__assert.rb +510 -0
- data/test/test__before_and_after.rb +117 -0
- data/test/test__before_and_after_all.rb +71 -0
- data/test/test__helpers.rb +197 -0
- data/test/test__raise.rb +104 -0
- data/test/test__skip.rb +41 -0
- data/test/test__throw.rb +103 -0
- metadata +196 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
class MiniSpec::Mocks::Validations
|
2
|
+
|
3
|
+
# extending expectation by expecting a specific returned value
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# expect(obj).to_receive(:a).and_return(1)
|
7
|
+
# # for this to pass `obj.a` should return 1
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# expect(obj).to_receive(:a, :b).and_return(1, 2)
|
11
|
+
# # for this to pass `obj.a` should return 1 and `obj.b` should return 2
|
12
|
+
#
|
13
|
+
# @example using a block to validate returned value
|
14
|
+
# expect(obj).to_receive(:a).and_return {|v| v == 1}
|
15
|
+
# # for this to pass `obj.a` should return 1
|
16
|
+
#
|
17
|
+
def and_return *expected, &block
|
18
|
+
return self if @failed
|
19
|
+
assert_given_arguments_match_received_messages(*expected, &block)
|
20
|
+
received = returned_values
|
21
|
+
|
22
|
+
if block
|
23
|
+
return @base.instance_exec(*received.values, &block) ||
|
24
|
+
returned_value_error!(@expected_messages, block, received)
|
25
|
+
end
|
26
|
+
|
27
|
+
expected = zipper(@expected_messages, expected)
|
28
|
+
received.each_pair do |msg,values|
|
29
|
+
# each message should return expected value at least once
|
30
|
+
values.any? {|v| validate_returned_value(expected[msg], v)} ||
|
31
|
+
returned_value_error!(msg, expected[msg], msg => values)
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
alias and_returned and_return
|
36
|
+
|
37
|
+
private
|
38
|
+
def returned_values
|
39
|
+
@expected_messages.inject({}) do |map,msg|
|
40
|
+
map.merge(msg => @messages[msg] ? @messages[msg].map {|m| m[:returned]} : [])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_returned_value expected, returned
|
45
|
+
if expected.is_a?(Regexp)
|
46
|
+
return returned.is_a?(Regexp) ? expected == returned : returned.to_s =~ expected
|
47
|
+
end
|
48
|
+
expected == returned
|
49
|
+
end
|
50
|
+
|
51
|
+
def returned_value_error! message, expected, received
|
52
|
+
fail_with("%s received %s message(s) and returned unexpected value(s).\nExpected: %s\nActual: %s" % [
|
53
|
+
pp(@object),
|
54
|
+
pp(message),
|
55
|
+
expected.is_a?(Proc) ?
|
56
|
+
'to pass validation at %s' % pp(source(expected)) :
|
57
|
+
pp(expected),
|
58
|
+
stringify_returned_values(received)
|
59
|
+
])
|
60
|
+
end
|
61
|
+
|
62
|
+
def stringify_returned_values returned
|
63
|
+
returned.is_a?(Hash) || raise(ArgumentError, 'a Hash expected')
|
64
|
+
returned.map do |msg,values|
|
65
|
+
values.each_with_index.map do |value,i|
|
66
|
+
'%s call #%s returned %s' % [
|
67
|
+
pp(msg),
|
68
|
+
i + 1,
|
69
|
+
pp(value)
|
70
|
+
]
|
71
|
+
end*"\n "
|
72
|
+
end*"\n "
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class MiniSpec::Mocks::Validations
|
2
|
+
# checks whether received message throws expected symbol
|
3
|
+
#
|
4
|
+
# @note you can match against thrown symbol but not against value.
|
5
|
+
# this is a WONTFIX limitation. though it is doable
|
6
|
+
# this would introduce a new layer of unproven complexity.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# expect(obj).to_receive(:a).and_throw(:something)
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# expect(obj).to_receive(:a, :b).and_throw(:A, :B)
|
13
|
+
# # for this to pass `obj.a` should throw :A and `obj.b` :B
|
14
|
+
#
|
15
|
+
def and_throw *expected, &block
|
16
|
+
return self if @failed
|
17
|
+
expected.all? {|x| x.is_a?(Symbol)} || raise(ArgumentError, '`and_throw` accepts only symbols')
|
18
|
+
# `and_throw` can be called without arguments
|
19
|
+
expected.empty? || assert_given_arguments_match_received_messages(*expected, &block)
|
20
|
+
received = thrown_symbols
|
21
|
+
|
22
|
+
if block
|
23
|
+
return @base.instance_exec(*received.values, &block) ||
|
24
|
+
throw_error!(@expected_messages, block, received)
|
25
|
+
end
|
26
|
+
|
27
|
+
expected = zipper(@expected_messages, expected)
|
28
|
+
received.each_pair do |msg,calls|
|
29
|
+
# each message should throw expected symbol at least once.
|
30
|
+
# if no specific symbol expected, check whether any symbol thrown.
|
31
|
+
calls.any? {|s| expected[msg] ? s == expected[msg] : s.is_a?(Symbol)} ||
|
32
|
+
throw_error!(msg, expected[msg], msg => calls)
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
alias and_thrown and_throw
|
37
|
+
alias and_thrown? and_throw
|
38
|
+
|
39
|
+
# assure received message does not throw a symbol
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# expect(obj).to_receive(:a).without_throw
|
43
|
+
#
|
44
|
+
def without_throw
|
45
|
+
return self if @failed
|
46
|
+
thrown_symbols.each_pair do |msg,calls|
|
47
|
+
calls.any? {|x| x.is_a?(Symbol)} && unexpected_throw_error!(msg, calls)
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def thrown_symbols
|
54
|
+
@expected_messages.inject({}) do |map,msg|
|
55
|
+
map.merge(msg => @messages[msg] ? @messages[msg].map {|m| extract_thrown_symbol(m[:raised])} : [])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def unexpected_throw_error! message, received
|
60
|
+
fail_with("%s received %s message(s) and thrown an unexpected symbol.\nExpected: %s\nActual: %s" % [
|
61
|
+
pp(@object),
|
62
|
+
pp(message),
|
63
|
+
'nothing to be thrown',
|
64
|
+
stringify_thrown_symbols(message => received)
|
65
|
+
])
|
66
|
+
end
|
67
|
+
|
68
|
+
def throw_error! message, expected, received
|
69
|
+
fail_with("%s received %s message(s) but did not throw accordingly.\nExpected: %s\nActual: %s" % [
|
70
|
+
pp(@object),
|
71
|
+
pp(message),
|
72
|
+
expected.is_a?(Proc) ?
|
73
|
+
'results to be validated at %s' % pp(source(expected)) :
|
74
|
+
pp(expected),
|
75
|
+
stringify_thrown_symbols(received)
|
76
|
+
])
|
77
|
+
end
|
78
|
+
|
79
|
+
def stringify_thrown_symbols received
|
80
|
+
received.is_a?(Hash) || raise(ArgumentError, 'a Hash expected')
|
81
|
+
received.map do |msg,calls|
|
82
|
+
calls.each_with_index.map do |call,i|
|
83
|
+
'%s call #%s thrown %s' % [
|
84
|
+
pp(msg),
|
85
|
+
i + 1,
|
86
|
+
call.is_a?(Symbol) ? pp(call) : 'nothing'
|
87
|
+
]
|
88
|
+
end*"\n "
|
89
|
+
end*"\n "
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
class MiniSpec::Mocks::Validations
|
2
|
+
class AnyYield; end
|
3
|
+
|
4
|
+
# extending expectation by expecting received message to yield
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Apple
|
8
|
+
#
|
9
|
+
# def color
|
10
|
+
# yield
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# def taste
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# describe Apple do
|
18
|
+
# testing :color do
|
19
|
+
# apple = Apple.new
|
20
|
+
#
|
21
|
+
# expect(apple).to_receive(:color).and_yield # => will pass
|
22
|
+
# expect(apple).to_receive(:taste).and_yield # => will fail
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# class Apple
|
28
|
+
#
|
29
|
+
# def color
|
30
|
+
# yield 1, 2
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# describe Apple do
|
35
|
+
# testing :color do
|
36
|
+
# apple = Apple.new
|
37
|
+
#
|
38
|
+
# expect(apple).to_receive(:color).and_yield(1, 2) # => will pass
|
39
|
+
# expect(apple).to_receive(:taste).and_yield(:something) # => will fail
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def and_yield *expected, &block
|
44
|
+
return self if @failed
|
45
|
+
# `and_yield` can be called without arguments
|
46
|
+
expected.empty? || assert_given_arguments_match_received_messages(*expected, &block)
|
47
|
+
received = yielded_values
|
48
|
+
|
49
|
+
if block
|
50
|
+
return @base.instance_exec(*received.values, &block) ||
|
51
|
+
yield_error!(@expected_messages, block, received)
|
52
|
+
end
|
53
|
+
|
54
|
+
single_message_expected? ?
|
55
|
+
validate_yields(expected, received) :
|
56
|
+
validate_yields_list(expected, received)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
alias and_yielded and_yield
|
60
|
+
alias and_yielded? and_yield
|
61
|
+
|
62
|
+
# make sure received message wont yield
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# expect(:obj).to_receive(:a).without_yield
|
66
|
+
#
|
67
|
+
def without_yield
|
68
|
+
return self if @failed
|
69
|
+
yielded_values.each_pair do |msg,calls|
|
70
|
+
next if calls.all?(&:nil?)
|
71
|
+
unexpected_yield_error!(msg, calls)
|
72
|
+
end
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def yielded_values
|
78
|
+
@expected_messages.inject({}) do |map,msg|
|
79
|
+
map.merge(msg => @messages[msg] ? @messages[msg].map {|m| m[:yielded]} : [])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_yields expected, received
|
84
|
+
message = @expected_messages[0]
|
85
|
+
calls = received[message]
|
86
|
+
return if validate_yields_calls(calls, expected)
|
87
|
+
yield_error!(message, expected, message => calls)
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_yields_list expected, received
|
91
|
+
expected = zipper(@expected_messages, expected)
|
92
|
+
received.each_pair do |msg,calls|
|
93
|
+
expect = Array(expected[msg]).flatten(1)
|
94
|
+
next if validate_yields_calls(calls, expect)
|
95
|
+
yield_error!(msg, expect, msg => calls)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_yields_calls calls, expected
|
100
|
+
expected.nil? || expected.empty? ?
|
101
|
+
calls.any? {|c| c.is_a?(Array)} :
|
102
|
+
calls.any? {|c| c == expected}
|
103
|
+
end
|
104
|
+
|
105
|
+
def unexpected_yield_error! message, received
|
106
|
+
fail_with("%s received %s message and unexpectedly yielded.\nExpected: %s\nActual: %s" % [
|
107
|
+
pp(@object),
|
108
|
+
pp(message),
|
109
|
+
'nothing to be yielded',
|
110
|
+
stringify_received_yields(message => received)
|
111
|
+
])
|
112
|
+
end
|
113
|
+
|
114
|
+
def yield_error! message, expected, received
|
115
|
+
fail_with("%s received %s message(s) but did not yield accordingly.\nExpected: %s\nActual: %s" % [
|
116
|
+
pp(@object),
|
117
|
+
pp(message),
|
118
|
+
stringify_expected_yields(expected),
|
119
|
+
stringify_received_yields(received)
|
120
|
+
])
|
121
|
+
end
|
122
|
+
|
123
|
+
def stringify_expected_yields expected
|
124
|
+
return 'yielded values to pass validation at %s' % pp(source(expected)) if expected.is_a?(Proc)
|
125
|
+
return 'something to be yielded' if expected.empty?
|
126
|
+
pp(expected)
|
127
|
+
end
|
128
|
+
|
129
|
+
def stringify_received_yields received
|
130
|
+
received.is_a?(Hash) || raise(ArgumentError, 'a Hash expected')
|
131
|
+
received.map do |msg,calls|
|
132
|
+
calls.each_with_index.map do |call,i|
|
133
|
+
'%s call #%s yielded %s' % [
|
134
|
+
pp(msg),
|
135
|
+
i + 1,
|
136
|
+
call ? pp(call) : 'nothing'
|
137
|
+
]
|
138
|
+
end*"\n "
|
139
|
+
end*"\n "
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module MiniSpec
|
2
|
+
class Proxy
|
3
|
+
|
4
|
+
@@negations = [
|
5
|
+
:not,
|
6
|
+
:to_not,
|
7
|
+
:has_not,
|
8
|
+
:have_not,
|
9
|
+
:does_not,
|
10
|
+
:did_not,
|
11
|
+
:is_not,
|
12
|
+
:is_not_a,
|
13
|
+
:wont,
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
# initializes a new proxy instance
|
17
|
+
# that will forward all received messages to tested object.
|
18
|
+
#
|
19
|
+
# @param base spec instance
|
20
|
+
# @param left_method the method on spec instance that accepts tested object,
|
21
|
+
# eg: is(...), does(...) etc.
|
22
|
+
# @param left_object tested object itself
|
23
|
+
# @param negation if set to a positive value assertion will be marked as failed if passed
|
24
|
+
# @param &proc if block given, it will be yielded(at a later point)
|
25
|
+
# and returned value will be used as tested object.
|
26
|
+
def initialize *args, &proc
|
27
|
+
@base, @left_method, @left_object, @negation, @failure_message = args
|
28
|
+
@left_proc = proc
|
29
|
+
@sugar = []
|
30
|
+
end
|
31
|
+
|
32
|
+
instance_methods.each do |m|
|
33
|
+
# overriding all instance methods so they point to tested object
|
34
|
+
# rather than to proxy instance.
|
35
|
+
# simply returns if no spec instance set.
|
36
|
+
#
|
37
|
+
# @example checking whether `foo` if frozen.
|
38
|
+
# is(:foo).frozen?
|
39
|
+
# # `is` will initialize and return a MiniSpec::Proxy instance with :foo passed into it.
|
40
|
+
# # MiniSpec::Proxy instance is receiving `frozen?` message and sending it to :foo.
|
41
|
+
#
|
42
|
+
define_method m do |*a, &p|
|
43
|
+
@base && __ms__assert(m, *a, &p)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# any missing method will be forwarded to #__ms__assert.
|
48
|
+
# simply returns if no spec instance set.
|
49
|
+
#
|
50
|
+
# @example checking whether `some_array` include `foo`
|
51
|
+
# does(some_array).include? foo
|
52
|
+
# # MiniSpec::Proxy instance does not respond to `include?`, so it is passed to `some_array`
|
53
|
+
def method_missing m, *a, &p
|
54
|
+
@base && __ms__assert(m, *a, &p)
|
55
|
+
end
|
56
|
+
|
57
|
+
%w[
|
58
|
+
a
|
59
|
+
is
|
60
|
+
is_a
|
61
|
+
are
|
62
|
+
will
|
63
|
+
was
|
64
|
+
does
|
65
|
+
did
|
66
|
+
have
|
67
|
+
has
|
68
|
+
to
|
69
|
+
be
|
70
|
+
been
|
71
|
+
].each do |m|
|
72
|
+
# sugar methods that returns proxy instance.
|
73
|
+
#
|
74
|
+
# @example `a` serve as a bridge between tested object and `instance_of?` message
|
75
|
+
# is(foo).a.instance_of?(Foo)
|
76
|
+
#
|
77
|
+
# @return [MiniSpec::Proxy] proxy instance
|
78
|
+
define_method(m) { @sugar.push(m); self }
|
79
|
+
end
|
80
|
+
|
81
|
+
# sugar methods that sets negation bit and returns proxy instance.
|
82
|
+
#
|
83
|
+
# @example `is_not_a` will set negation bit and return current proxy instance.
|
84
|
+
# assure(this).is_not_a.instance_of? That
|
85
|
+
#
|
86
|
+
# @return [MiniSpec::Proxy] proxy instance
|
87
|
+
@@negations.each do |verb|
|
88
|
+
define_method(verb) { @negation = true; self }
|
89
|
+
end
|
90
|
+
|
91
|
+
# the core of MiniSpec assertion methodology.
|
92
|
+
# all tested objects arrives this point where they receive testing messages.
|
93
|
+
#
|
94
|
+
# @param right_method message to be sent to tested object.
|
95
|
+
# if there is a helper with such a name, the helper are run and result returned.
|
96
|
+
# @param *args arguments to be passed to tested object when message sent.
|
97
|
+
# @param &right_proc block to be passed to tested object when message sent.
|
98
|
+
# @return if some helper matched first argument returns helper's execution result.
|
99
|
+
# returns `nil` if test passed.
|
100
|
+
# returns a failure if test failed.
|
101
|
+
def __ms__assert right_method, *args, &right_proc
|
102
|
+
if helper = @base.class.helpers[right_method]
|
103
|
+
return __ms__run_helper(helper, *args, &right_proc)
|
104
|
+
end
|
105
|
+
|
106
|
+
result = __ms__send(right_method, *args, &right_proc)
|
107
|
+
|
108
|
+
if @negation # sometimes
|
109
|
+
return unless result # verbosity
|
110
|
+
else # is
|
111
|
+
return if result # a
|
112
|
+
end # virtue
|
113
|
+
|
114
|
+
__ms__fail(right_method, right_proc, *args)
|
115
|
+
end
|
116
|
+
|
117
|
+
# passing received message to tested object
|
118
|
+
def __ms__send right_method, *args, &right_proc
|
119
|
+
__ms__left_object.__send__(right_method, *args, &right_proc)
|
120
|
+
end
|
121
|
+
|
122
|
+
# executes a helper block earlier defined at class level
|
123
|
+
#
|
124
|
+
# @param helper helper name
|
125
|
+
# @param *args arguments to be passed into helper block
|
126
|
+
def __ms__run_helper helper, *args, &right_proc
|
127
|
+
helper_proc, helper_opts = helper
|
128
|
+
args.unshift(@left_proc || @left_object)
|
129
|
+
args.push(right_proc) if right_proc
|
130
|
+
args << {
|
131
|
+
left_method: @left_method,
|
132
|
+
left_object: @left_object,
|
133
|
+
left_proc: @left_proc,
|
134
|
+
right_proc: right_proc,
|
135
|
+
negation: @negation
|
136
|
+
}.freeze if helper_opts[:with_context]
|
137
|
+
@base.__ms__inside_helper = true
|
138
|
+
@base.instance_exec(*args, &helper_proc)
|
139
|
+
ensure
|
140
|
+
@base.__ms__inside_helper = false
|
141
|
+
end
|
142
|
+
|
143
|
+
# computes tested object based on arguments passed at initialize.
|
144
|
+
# if a block given it is yielded and returned value used as tested object.
|
145
|
+
# otherwise orig `@left_object` used.
|
146
|
+
# if given block raises an error it will be rescued and returned as tested object.
|
147
|
+
def __ms__left_object
|
148
|
+
return @left_object_value if @left_object_computed
|
149
|
+
@left_object_computed = true
|
150
|
+
@left_object_value = begin
|
151
|
+
@left_proc ? @base.instance_exec(&@left_proc) : @left_object
|
152
|
+
rescue Exception => e
|
153
|
+
e
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# builds a MiniSpec failure and pass it to spec's #fail instance method.
|
158
|
+
# using splat cause it should be able to receive `nil` and `false` as second argument
|
159
|
+
# as well as work without second argument at all.
|
160
|
+
def __ms__fail right_method, right_proc, *args
|
161
|
+
right_object = right_proc ? \
|
162
|
+
__ms__proc_definition(right_method.to_s, right_proc) : \
|
163
|
+
(args.size > 0 ? args.first : :__ms__right_object)
|
164
|
+
failure = {
|
165
|
+
left_method: @left_method,
|
166
|
+
left_object: __ms__left_object,
|
167
|
+
right_method: (@sugar + [right_method])*' ',
|
168
|
+
right_object: right_object,
|
169
|
+
negation: @negation
|
170
|
+
}
|
171
|
+
failure[:message] = @failure_message if @failure_message
|
172
|
+
@base.send(:fail, failure)
|
173
|
+
end
|
174
|
+
|
175
|
+
# reads what follow after the given method at the line where given proc is defined
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
# assure([]).has.any? {|x| x > 1}
|
179
|
+
# # => {|x| x > 1}
|
180
|
+
#
|
181
|
+
# @return a string if proc is defined in a real file.
|
182
|
+
# `nil` otherwise (think of irb/pry)
|
183
|
+
def __ms__proc_definition meth, proc
|
184
|
+
return unless source = __ms__source_line(proc)
|
185
|
+
source = source.split(meth)[1..-1].map(&:strip).join(meth)
|
186
|
+
def source.inspect; self end
|
187
|
+
source
|
188
|
+
end
|
189
|
+
|
190
|
+
# reads the line at which given proc is defined.
|
191
|
+
#
|
192
|
+
# @return a string if file exists.
|
193
|
+
# `nil` if file does not exits(think of irb/pry)
|
194
|
+
def __ms__source_line proc
|
195
|
+
file, line = proc.source_location
|
196
|
+
return unless lines = MiniSpec.source_location_cache(file)
|
197
|
+
(line = lines[line - 1]) && line.strip
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|