Module: MiniSpec::InstanceAPI
- Defined in:
- lib/minispec/api/instance.rb,
lib/minispec/api/instance/mocks/mocks.rb,
lib/minispec/api/instance/mocks/spies.rb,
lib/minispec/api/instance/mocks/stubs.rb,
lib/minispec/api/instance/mocks/doubles.rb
Instance Attribute Summary collapse
- #__ms__failures ⇒ Array readonly
-
#__ms__inside_helper ⇒ Object
Returns the value of attribute __ms__inside_helper.
Instance Method Summary collapse
-
#__ms__boot ⇒ Object
runs before any tests.
-
#__ms__halt ⇒ Object
runs after all tests finished.
-
#__ms__mocks__define_regular_proxy(object, method_name, visibility) ⇒ Object
replaces given method with a proxy that collects received messages and calls the original method.
-
#__ms__mocks__define_singleton_proxy(object, method_name) ⇒ Object
defines a singleton proxy that collects received messages and calls the original method.
-
#__ms__mocks__define_void_proxy(object, method_name) ⇒ Object
defines a singleton proxy that collects received messages and calls nothing.
-
#__ms__mocks__instance_messages(object) ⇒ Object
takes a copy of received messages and returns only messages received by given object.
-
#__ms__mocks__messages_copy ⇒ Object
it is critical to iterate over a “statical” copy of messages array, otherwise iteration will generate a uncatchable infinite loop when messages array are updated during iteration.
-
#__ms__mocks__method_missing_proxy(object, method) ⇒ Object
replace ‘method_missing` method with a proxy that collects received messages and calls original `method_missing` method.
-
#__ms__mocks__regular_proxy(object, method_name, method = nil) ⇒ Object
returns a proc to be used with ‘define_method`.
- #__ms__mocks__reset_variables ⇒ Object
-
#__ms__mocks__restore_originals ⇒ Object
restoring stubbed methods.
- #__ms__mocks__validate_expectations ⇒ Object
-
#__ms__prepare_test ⇒ Object
private
setting/resetting all necessary instance variables for a test to run in clean state.
- #__ms__run_test(label) ⇒ Object
- #__ms__skipped? ⇒ Boolean
- #chained_stub(object, chain, visibility = nil, &block) ⇒ Object
-
#double(*args, &proc) ⇒ Object
creates a double object.
-
#fail(failure = {}) ⇒ Array
(also: #fail!)
adds a new failure to the stack.
- #hash_stub(object, hash, visibility = nil, &proc) ⇒ Object
-
#mock(object, method, visibility = nil, &proc) ⇒ Object
the mock is basically a stub with difference it will also add a expectation.
-
#mocks(object, *methods, &proc) ⇒ Object
mocking multiple methods at once.
- #private_mock(object, method, &proc) ⇒ Object
- #private_mocks(object, *methods, &proc) ⇒ Object
-
#private_stub(object, stub, &proc) ⇒ Object
same as stub except it defines private stubs (@see #stub).
- #private_stubs(object, *stubs, &proc) ⇒ Object
- #protected_mock(object, method, &proc) ⇒ Object
- #protected_mocks(object, *methods, &proc) ⇒ Object
-
#protected_stub(object, stub, &proc) ⇒ Object
same as stub except it defines protected stubs (@see #stub).
- #protected_stubs(object, *stubs, &proc) ⇒ Object
-
#proxy(object, method_name) ⇒ Object
overriding given method of given object with a proxy so MiniSpec can later check whether given method was called.
-
#public_mock(object, method, &proc) ⇒ Object
same as ‘mock` except it will enforce public visibility on mocked method.
- #public_mocks(object, *methods, &proc) ⇒ Object
-
#public_stub(object, stub, &proc) ⇒ Object
stubbing a method and enforce public visibility on it.
- #public_stubs(object, *stubs, &proc) ⇒ Object
-
#skip ⇒ Object
(also: #skip!)
stop evaluation of the current test right away.
-
#spy(object, *methods) ⇒ Object
basically by proxying an object we attach a spy on it so any received messages will be reported.
-
#stub(object, stub, visibility = nil, &proc) ⇒ Object
stubbing given method and keeps original visibility.
-
#stubs(object, *stubs, &proc) ⇒ Object
same as ‘stub` except it defines multiple stubs at once.
-
#subject ⇒ Object
this will be overridden when the spec are defined using Minispec’s DSL.
Instance Attribute Details
#__ms__failures ⇒ Array (readonly)
5 6 7 |
# File 'lib/minispec/api/instance.rb', line 5 def __ms__failures @__ms__failures end |
#__ms__inside_helper ⇒ Object
Returns the value of attribute __ms__inside_helper.
7 8 9 |
# File 'lib/minispec/api/instance.rb', line 7 def __ms__inside_helper @__ms__inside_helper end |
Instance Method Details
#__ms__boot ⇒ Object
runs before any tests
145 146 147 148 |
# File 'lib/minispec/api/instance.rb', line 145 def __ms__boot __ms__prepare_test (hook = self.class.before_all?) && self.instance_exec(&hook) end |
#__ms__halt ⇒ Object
runs after all tests finished. runs unconditionally even when there are failed tests.
152 153 154 |
# File 'lib/minispec/api/instance.rb', line 152 def __ms__halt (hook = self.class.after_all?) && self.instance_exec(&hook) end |
#__ms__mocks__define_regular_proxy(object, method_name, visibility) ⇒ Object
replaces given method with a proxy that collects received messages and calls the original method.
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 120 def __ms__mocks__define_regular_proxy object, method_name, visibility method = object.method(method_name).unbind method = __ms__mocks__regular_proxy(object, method_name, method) extender = Module.new do define_method(method_name, &method) private method_name if visibility == :private protected method_name if visibility == :protected end object.extend(extender) end |
#__ms__mocks__define_singleton_proxy(object, method_name) ⇒ Object
defines a singleton proxy that collects received messages and calls the original method.
136 137 138 139 140 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 136 def __ms__mocks__define_singleton_proxy object, method_name method = object.method(method_name).unbind method = __ms__mocks__regular_proxy(object, method_name, method) object.define_singleton_method(method_name, &method) end |
#__ms__mocks__define_void_proxy(object, method_name) ⇒ Object
registering methods added this way so they can be undefined after test run
defines a singleton proxy that collects received messages and calls nothing.
149 150 151 152 153 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 149 def __ms__mocks__define_void_proxy object, method_name (@__ms__stubs__originals[object] ||= {})[method_name] = [] method = __ms__mocks__regular_proxy(object, method_name) object.define_singleton_method(method_name, &method) end |
#__ms__mocks__instance_messages(object) ⇒ Object
takes a copy of received messages and returns only messages received by given object
319 320 321 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 319 def object .select {|m| m[:object] == object}.freeze end |
#__ms__mocks__messages_copy ⇒ Object
it is critical to iterate over a “statical” copy of messages array, otherwise iteration will generate a uncatchable infinite loop when messages array are updated during iteration.
311 312 313 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 311 def @__ms__messages.dup end |
#__ms__mocks__method_missing_proxy(object, method) ⇒ Object
replace ‘method_missing` method with a proxy that collects received messages and calls original `method_missing` method. stat are collected for two methods:
1. `method_missing` itself
2. method what `method_missing` received as first argument
stat has same format as on ‘__ms__mocks__regular_proxy`
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 220 def __ms__mocks__method_missing_proxy object, method = @__ms__messages Proc.new do |meth, *args, &block| = { object: object, method: :method_missing, arguments: [meth, *args], caller: Array(caller) } .push() = {object: object, method: meth, arguments: args} .push() proc = if block Proc.new do |*a,&b| [:yielded] = a block.call(*a,&b) end else nil end begin [:returned] = method.bind(self).call(meth, *args, &proc) rescue Exception => e [:raised] = e end .freeze [:raised] ? raise([:raised]) : [:returned] end end |
#__ms__mocks__regular_proxy(object, method_name, method = nil) ⇒ Object
returns a proc to be used with ‘define_method`. the proc will collect received messages then will call original method, if any.
messages are stored into ‘@__ms__messages` Array each single message looks like:
{object: ..., method: ..., arguments: ..., returned: ..., raised: ..., yielded: ...}
‘:returned` key are filled if original method called and it does not throw nor raise. `:raised` key are filled if original method called and it raises an error. `:yielded` key are filled if original method called with a block that was yielded.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 171 def __ms__mocks__regular_proxy object, method_name, method = nil method_name.is_a?(Symbol) || raise(ArgumentError, 'method name should be a Symbol') if :method_missing == method_name return __ms__mocks__method_missing_proxy(object, method) end = @__ms__messages Proc.new do |*args, &block| = { object: object, method: method_name, arguments: args, caller: Array(caller) } .push() return self == nil if method_name == :nil? return unless method proc = if block Proc.new do |*a,&b| [:yielded] = a block.call(*a,&b) end else nil end begin [:returned] = method.bind(self).call(*args, &proc) rescue Exception => e [:raised] = e end .freeze [:raised] ? raise([:raised]) : [:returned] end end |
#__ms__mocks__reset_variables ⇒ Object
105 106 107 108 109 110 111 |
# File 'lib/minispec/api/instance.rb', line 105 def __ms__mocks__reset_variables @__ms__messages = [] @__ms__proxies = {} @__ms__stubs = {} @__ms__stubs__originals = {} @__ms__expectations = [] end |
#__ms__mocks__restore_originals ⇒ Object
restoring stubbed methods.
it processes ‘@__ms__stubs__originals` Hash where keys are the objects and values are the object’s methods to be restored. each value is a Array where first element is the method name and the second element is what previous method was.
-
if second element is an empty Array that mean method were not defined before stubbing, so simply undefine it.
-
if second element is a Array with last element set to :singleton Symbol, the method was a singleton before stubbing it, so defining a singleton method using second element’s first element.
-
if second element is a Array with last element set to any of :public, :protected, :private Symbol an method with according visibility will be defined using second element’s first element.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 281 def __ms__mocks__restore_originals return unless stubs = @__ms__stubs__originals stubs.each_pair do |object, methods| methods.each_pair do |method_name, method| # clearing proxies cache so the method can be proxied again during current test (x = @__ms__proxies[object]) && x.delete(method_name) if method.last.nil? MiniSpec::Utils.undefine_method(object, method_name) elsif method.last == :singleton object.define_singleton_method(method_name, &method.first) else extender = Module.new do define_method(method_name, &method.first) private method_name if method.last == :private protected method_name if method.last == :protected end object.extend(extender) end end end # clearing cache for cases when this run during current test stubs.clear end |
#__ms__mocks__validate_expectations ⇒ Object
323 324 325 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 323 def __ms__mocks__validate_expectations catch(:__ms__stop_evaluation) { @__ms__expectations.each(&:validate!) } end |
#__ms__prepare_test ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
setting/resetting all necessary instance variables for a test to run in clean state
98 99 100 101 102 103 |
# File 'lib/minispec/api/instance.rb', line 98 def __ms__prepare_test @__ms__vars = {} @__ms__failures = [] @__ms__skipped = nil __ms__mocks__reset_variables end |
#__ms__run_test(label) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/minispec/api/instance.rb', line 113 def __ms__run_test label Minispec.tests += 1 __ms__prepare_test runner = proc do # running :before hooks, if any self.class.before?(label).each {|(l,m,b)| instance_exec(l,m,&b)} # running test catch :__ms__stop_evaluation do instance_exec(&self.class.tests[label].last) end # running :after hooks, if any self.class.after?(label).each {|(l,m,b)| instance_exec(l,m,&b)} end if around = self.class.around?(label).last self.instance_exec(runner, &around.last) else runner.call end __ms__mocks__validate_expectations __ms__mocks__restore_originals @__ms__failures rescue Exception => e [e] ensure __ms__mocks__reset_variables end |
#__ms__skipped? ⇒ Boolean
66 |
# File 'lib/minispec/api/instance.rb', line 66 def __ms__skipped?; @__ms__skipped end |
#chained_stub(object, chain, visibility = nil, &block) ⇒ Object
52 53 54 55 56 57 58 59 60 61 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 52 def chained_stub object, chain, visibility = nil, &block chain = chain.split('.').map(&:to_sym) base, last_index = self, chain.size - 1 chain.each_with_index do |m,i| next_object = (i == last_index ? nil : Struct.new(chain[i+1]).new) return stub(object, m, visibility, &block) unless next_object stub(object, m, visibility) { next_object } object = next_object end end |
#double(*args, &proc) ⇒ Object
creates a double object. if one or more arguments given, first argument will be used as name, unless it is a Hash. arguments that goes after first one are treated as stubs.
22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/minispec/api/instance/mocks/doubles.rb', line 22 def double *args, &proc name = args.first.is_a?(Hash) ? nil : args.shift object = Object.new object.define_singleton_method(:__ms__double_instance) {true} object.define_singleton_method(:inspect) {name} if name hashes, rest = args.partition {|s| s.is_a?(Hash)} hashes.each {|h| stub(object, h)} rest.each {|s| stub(object, s, &proc)} object end |
#fail(failure = {}) ⇒ Array Also known as: fail!
adds a new failure to the stack. a failure is a Hash containing keys like
:message
:left_method
:left_object
:right_method
:right_object
:negation
:callers
used by MiniSpec::Run#failures_summary and MiniSpecRun#failure_message to output useful info about failed tests.
84 85 86 87 88 89 90 91 92 |
# File 'lib/minispec/api/instance.rb', line 84 def fail failure = {} return unless failure unless failure.is_a?(Hash) failure || raise(ArgumentError, 'Please provide a failure message') failure = {message: failure} end @__ms__failures << failure.merge(callers: @__ms__callers) throw :__ms__stop_evaluation unless self.class.continue_on_failures? end |
#hash_stub(object, hash, visibility = nil, &proc) ⇒ Object
41 42 43 44 45 46 47 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 41 def hash_stub object, hash, visibility = nil, &proc proc && raise(ArgumentError, 'Both Hash and block given. Please use either one.') hash.each_pair do |s,v| stub(object, s, visibility, &proc {v}) end return MiniSpec::Mocks::HashedStub end |
#mock(object, method, visibility = nil, &proc) ⇒ Object
if mocked method exists it’s visibility will be kept
the mock is basically a stub with difference it will also add a expectation. that’s it, a mock will stub a method on a object and will expect that stub to be called before test finished.
the ‘mock` method will return the actual stub so you can build chained constraints on it.
mock(some_object, :some_method).
with(:a, :b) { 'called with a, b' }.
with(:x, :y) { 'called with x, y' }.
with_any { 'whatever' }
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 25 def mock object, method, visibility = nil, &proc if method.is_a?(Hash) proc && raise(ArgumentError, 'Both Hash and block given. Please use either one.') method.each_pair {|m,r| mock(object, m, visibility, &proc {r})} return MiniSpec::Mocks::HashedStub end visibility ||= MiniSpec::Utils.method_visibility(object, method) || :public # IMPORTANT! stub should be defined before expectation stub = stub(object, method, visibility, &proc) expect(object).to_receive(method) stub end |
#mocks(object, *methods, &proc) ⇒ Object
mocking multiple methods at once
45 46 47 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 45 def mocks object, *methods, &proc MiniSpec::Mocks::MultipleStubsProxy.new(methods.map {|m| mock(object, m, &proc)}) end |
#private_mock(object, method, &proc) ⇒ Object
66 67 68 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 66 def private_mock object, method, &proc mock(object, method, :private, &proc) end |
#private_mocks(object, *methods, &proc) ⇒ Object
70 71 72 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 70 def private_mocks object, *methods, &proc MiniSpec::Mocks::MultipleStubsProxy.new(methods.map {|m| mock(object, m, :private, &proc)}) end |
#private_stub(object, stub, &proc) ⇒ Object
same as stub except it defines private stubs (@see #stub)
97 98 99 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 97 def private_stub object, stub, &proc stub(object, stub, :private, &proc) end |
#private_stubs(object, *stubs, &proc) ⇒ Object
101 102 103 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 101 def private_stubs object, *stubs, &proc MiniSpec::Mocks::MultipleStubsProxy.new(stubs.map {|s| private_stub(object, s, &proc)}) end |
#protected_mock(object, method, &proc) ⇒ Object
58 59 60 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 58 def protected_mock object, method, &proc mock(object, method, :protected, &proc) end |
#protected_mocks(object, *methods, &proc) ⇒ Object
62 63 64 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 62 def protected_mocks object, *methods, &proc MiniSpec::Mocks::MultipleStubsProxy.new(methods.map {|m| mock(object, m, :protected, &proc)}) end |
#protected_stub(object, stub, &proc) ⇒ Object
same as stub except it defines protected stubs (@see #stub)
87 88 89 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 87 def protected_stub object, stub, &proc stub(object, stub, :protected, &proc) end |
#protected_stubs(object, *stubs, &proc) ⇒ Object
91 92 93 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 91 def protected_stubs object, *stubs, &proc MiniSpec::Mocks::MultipleStubsProxy.new(stubs.map {|s| protected_stub(object, s, &proc)}) end |
#proxy(object, method_name) ⇒ Object
doubles and stubs will be skipped as they are already proxified
overriding given method of given object with a proxy so MiniSpec can later check whether given method was called.
if given method does not exists a NoMethodError raised
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 87 def proxy object, method_name # do not proxify doubles return if object.respond_to?(:__ms__double_instance) # do not proxify stubs return if (x = @__ms__stubs__originals) && (x = x[object]) && x[method_name] proxies = (@__ms__proxies[object] ||= []) return if proxies.include?(method_name) proxies << method_name # method exists and it is a singleton. # `nil?` method can be overridden only through a singleton if method_name == :nil? || object.singleton_methods.include?(method_name) return __ms__mocks__define_singleton_proxy(object, method_name) end # method exists and it is not a singleton, define a regular proxy if visibility = MiniSpec::Utils.method_visibility(object, method_name) return __ms__mocks__define_regular_proxy(object, method_name, visibility) end raise(NoMethodError, '%s does not respond to %s. Can not proxify an un-existing method.' % [ object.inspect, method_name.inspect ]) end |
#public_mock(object, method, &proc) ⇒ Object
same as ‘mock` except it will enforce public visibility on mocked method.
50 51 52 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 50 def public_mock object, method, &proc mock(object, method, :public, &proc) end |
#public_mocks(object, *methods, &proc) ⇒ Object
54 55 56 |
# File 'lib/minispec/api/instance/mocks/mocks.rb', line 54 def public_mocks object, *methods, &proc MiniSpec::Mocks::MultipleStubsProxy.new(methods.map {|m| public_mock(object, m, &proc)}) end |
#public_stub(object, stub, &proc) ⇒ Object
stubbing a method and enforce public visibility on it. that’s it, even if method exists and it is not public, after stubbing it will become public.
77 78 79 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 77 def public_stub object, stub, &proc stub(object, stub, :public, &proc) end |
#public_stubs(object, *stubs, &proc) ⇒ Object
81 82 83 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 81 def public_stubs object, *stubs, &proc MiniSpec::Mocks::MultipleStubsProxy.new(stubs.map {|s| public_stub(object, s, &proc)}) end |
#skip ⇒ Object Also known as: skip!
stop evaluation of the current test right away
60 61 62 63 |
# File 'lib/minispec/api/instance.rb', line 60 def skip @__ms__skipped = caller.first throw :__ms__stop_evaluation end |
#spy(object, *methods) ⇒ Object
basically by proxying an object we attach a spy on it so any received messages will be reported
13 14 15 |
# File 'lib/minispec/api/instance/mocks/spies.rb', line 13 def spy object, *methods methods.each {|method| proxy(object, method)} end |
#stub(object, stub, visibility = nil, &proc) ⇒ Object
stubbing given method and keeps original visibility
if a block given, it will receive original method as first argument and any passed parameters as rest arguments.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 21 def stub object, stub, visibility = nil, &proc [Symbol, String, Hash].include?(stub.class) || raise(ArgumentError, 'a Symbol, String or Hash expected') if stub.is_a?(Hash) return hash_stub(object, stub, visibility, &proc) elsif stub =~ /\./ return chained_stub(object, stub, visibility, &proc) end visibility ||= MiniSpec::Utils.method_visibility(object, stub) || :public stubs = (@__ms__stubs[object.__id__] ||= {}) stubs[stub] ||= MiniSpec::Mocks::Stub.new(object, @__ms__messages, @__ms__stubs__originals) stubs[stub].stubify(stub, visibility, &proc) stubs[stub] end |
#stubs(object, *stubs, &proc) ⇒ Object
same as ‘stub` except it defines multiple stubs at once
70 71 72 |
# File 'lib/minispec/api/instance/mocks/stubs.rb', line 70 def stubs object, *stubs, &proc MiniSpec::Mocks::MultipleStubsProxy.new(stubs.map {|s| stub(object, s, &proc)}) end |
#subject ⇒ Object
this will be overridden when the spec are defined using Minispec’s DSL
49 |
# File 'lib/minispec/api/instance.rb', line 49 def subject; end |