xspec 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Shay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-20 00:00:00.000000000 Z
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: yeah
14
14
  email:
@@ -21,13 +21,13 @@ files:
21
21
  - README.md
22
22
  - bin/xspec
23
23
  - lib/xspec.rb
24
- - lib/xspec/assertion_contexts.rb
25
24
  - lib/xspec/autorun.rb
26
25
  - lib/xspec/data_structures.rb
27
26
  - lib/xspec/defaults.rb
28
27
  - lib/xspec/dsl.rb
29
28
  - lib/xspec/evaluators.rb
30
29
  - lib/xspec/notifiers.rb
30
+ - lib/xspec/schedulers.rb
31
31
  - spec/all_specs.rb
32
32
  - spec/integration/rspec_expectations_spec.rb
33
33
  - spec/spec_helper.rb
@@ -49,7 +49,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 1.9.0
52
+ version: 2.1.0
53
53
  required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - ">="
@@ -1,382 +0,0 @@
1
- # # Assertion Contexts
2
-
3
- # Assertion contexts are composed together into a context stack. The final
4
- # stack has a single API method `call`, which is sent the unit of work to be
5
- # executed and must return an array of `Failure` objects. It should not allow
6
- # code-level exceptions to be raised, though should not block system exceptions
7
- # (`SignalException`, `NoMemoryError`, etc).
8
- module XSpec
9
- module AssertionContext
10
- # A stack is typically book-ended by the top and bottom contexts, so this
11
- # helper is the most commond way to build up a custom stack.
12
- def self.stack(&block)
13
- Module.new do
14
- include Bottom
15
- instance_exec &block
16
- include Top
17
- end
18
- end
19
-
20
- # The bottom context executes the unit of work, with no error handling or
21
- # extra behaviour. By separating this, all other contexts layered on top of
22
- # this one can just call `super`, making them easy to compose.
23
- module Bottom
24
- def call(unit_of_work)
25
- instance_exec(&unit_of_work.block)
26
- []
27
- end
28
- end
29
-
30
- # The top should be included as the final module in a context stack. It is
31
- # a catch all to make sure all standard exceptions have been handled and
32
- # do not leak outside the stack.
33
- module Top
34
- def call(unit_of_work)
35
- super
36
- rescue => e
37
- [CodeException.new(unit_of_work, e.message, e.backtrace)]
38
- end
39
- end
40
-
41
- # ### Simple Assertions
42
- #
43
- # This simple context provides very straight-forward assertion methods.
44
- module Simple
45
- class AssertionFailed < RuntimeError
46
- attr_reader :message, :backtrace
47
-
48
- def initialize(message, backtrace)
49
- @message = message
50
- @backtrace = backtrace
51
- end
52
- end
53
-
54
- def call(unit_of_work)
55
- super
56
- rescue AssertionFailed => e
57
- [Failure.new(unit_of_work, e.message, e.backtrace)]
58
- end
59
-
60
- def assert(proposition, message=nil)
61
- unless proposition
62
- message ||= 'assertion failed'
63
-
64
- _raise message
65
- end
66
- end
67
-
68
- def assert_equal(expected, actual)
69
- unless expected == actual
70
- message ||= <<-EOS.chomp
71
- want: #{expected.inspect}
72
- got: #{actual.inspect}
73
- EOS
74
-
75
- _raise message
76
- end
77
- end
78
-
79
- def assert_include(expected, output)
80
- assert output.include?(expected),
81
- "#{expected.inspect} not present in: #{output.inspect}"
82
- end
83
-
84
- def fail(message = nil)
85
- message ||= 'failed'
86
-
87
- _raise message
88
- end
89
-
90
- private
91
-
92
- def _raise(message)
93
- raise AssertionFailed.new(message, caller)
94
- end
95
- end
96
-
97
- # ### Doubles
98
- #
99
- # The doubles module provides test doubles that can be used in-place of
100
- # real objects.
101
- module Doubles
102
- DoubleFailure = Class.new(RuntimeError)
103
-
104
- def call(unit_of_work)
105
- super
106
- rescue DoubleFailure => e
107
- [Failure.new(unit_of_work, e.message, e.backtrace)]
108
- end
109
-
110
- # It can be configured with a few options:
111
- #
112
- # * `auto_verify` calls `assert_exhausted` on all created doubles after a
113
- # unit of work executes successfully to ensure that all expectations that
114
- # were set were actually called.
115
- # * `strict` forbids doubling of classes that have not been loaded. This
116
- # should generally be enabled when doing a full spec run, and disabled
117
- # when running specs in isolation.
118
- #
119
- # The `with` method returns a module that can be included in a stack.
120
- def self.with(*opts)
121
- modules = [self] + opts.map {|x| {
122
- auto_verify: AutoVerify,
123
- strict: Strict
124
- }.fetch(x) }
125
-
126
-
127
- Module.new do
128
- modules.each do |m|
129
- include m
130
- end
131
- end
132
- end
133
-
134
- # An instance double stands in for an instance of the given class
135
- # reference, given as a string. The class does not need to be loaded, but
136
- # if it is then only public instance methods defined on the class are
137
- # able to be expected.
138
- def instance_double(klass)
139
- _double(klass, InstanceReference)
140
- end
141
-
142
- # Simarly, a class double validates that class responds to all expected
143
- # methods, if that class has been loaded.
144
- def class_double(klass)
145
- _double(klass, ClassReference)
146
- end
147
-
148
- # If the doubled class has not been loaded, a null object reference is
149
- # used that allows expecting of all methods.
150
- def _double(klass, type)
151
- ref = if self.class.const_defined?(klass)
152
- type.new(self.class.const_get(klass))
153
- else
154
- StringReference.new(klass)
155
- end
156
-
157
- Double.new(ref)
158
- end
159
-
160
- # To set up an expectation on a double, call the expected method an
161
- # arguments on the proxy object returned by `expect`. If a return value
162
- # is desired, it can be supplied as a block, for example:
163
- # `expect(double).some_method(1, 2) { "return value" }`
164
- def expect(obj)
165
- Recorder.new(obj, :_expect)
166
- end
167
-
168
- # An allowed method will never be validated by `assert_exhausted`.
169
- # Matching expectations are always given precendence to allows.
170
- def allow(obj)
171
- Recorder.new(obj, :_allow)
172
- end
173
-
174
- class Recorder
175
- def initialize(double, method)
176
- @double = double
177
- @method = method
178
- end
179
-
180
- def method_missing(*args, &ret)
181
- @double.__send__(@method, args, &(ret || ->{}))
182
- end
183
- end
184
-
185
- # Since the double object inherits from `BasicObject`, virtually every
186
- # method call will be routed through `method_missing`. From there, the
187
- # call can be checked against the expectations that were setup at the
188
- # beginning of a spec.
189
- class Double < BasicObject
190
- def initialize(klass)
191
- @klass = klass
192
- @expected = []
193
- @allowed = []
194
- end
195
-
196
- def method_missing(*actual_args)
197
- expected_index = @expected.find_index {|expected_args, ret|
198
- expected_args == actual_args
199
- }
200
-
201
- matching_message = if expected_index
202
- @expected.delete_at(expected_index)
203
- else
204
- @allowed.detect {|expected_args, ret|
205
- expected_args == actual_args
206
- }
207
- end
208
-
209
- if matching_message
210
- matching_message[1].call
211
- else
212
- name, rest = *actual_args
213
- ::Kernel.raise DoubleFailure, "Unexpectedly received: %s(%s)" % [
214
- name,
215
- [*rest].map(&:inspect).join(", ")
216
- ]
217
- end
218
- end
219
-
220
- # The methods needed on this object to set it up and verify it are
221
- # prefixed by `_` to try to ensure they don't clash with any method
222
- # expectations. While not fail-safe, users should only be using
223
- # expectations for a public API, and `_` is traditionally only used
224
- # for private methods (if at all).
225
- def _expect(args, &ret)
226
- @klass.validate_call! args
227
-
228
- @expected << [args, ret]
229
- end
230
-
231
- def _allow(args, &ret)
232
- @klass.validate_call! args
233
-
234
- @allowed << [args, ret]
235
- end
236
-
237
- def _verify
238
- return if @expected.empty?
239
-
240
- ::Kernel.raise DoubleFailure, "%s double did not receive:\n%s" % [
241
- @klass.to_s,
242
- @expected.map {|(name, *args), _|
243
- " %s(%s)" % [name, args.map(&:inspect).join(", ")]
244
- }.join("\n")
245
- ]
246
- end
247
- end
248
-
249
- # A reference can be thought of as a "backing object" for a double. It
250
- # provides an API to validate that a method being expected actually
251
- # exists - the implementation is different for the different types of
252
- # doubles.
253
- class Reference
254
- def initialize(klass)
255
- @klass = klass
256
- end
257
-
258
- def validate_call!(args)
259
- end
260
-
261
- def to_s
262
- @klass.to_s
263
- end
264
- end
265
-
266
- # A string reference is the "null object" of references, allowing all
267
- # methods to be expected. It is used when nothing is known about the
268
- # referenced class (such as when it has not been loaded).
269
- class StringReference < Reference
270
- end
271
-
272
- # Class and Instance references are backed by loaded classes, and
273
- # restrict the messages that can be expected on a double.
274
- class ClassReference < Reference
275
- def validate_call!(args)
276
- name, rest = *args
277
-
278
- unless @klass.respond_to?(name)
279
- raise DoubleFailure,
280
- "#{@klass}.#{name} is unimplemented or not public"
281
- end
282
- end
283
- end
284
-
285
- class InstanceReference < Reference
286
- def validate_call!(args)
287
- name, rest = *args
288
-
289
- unless @klass.public_instance_methods.include?(name)
290
- raise DoubleFailure,
291
- "#{@klass}##{name} is unimplemented or not public"
292
- end
293
- end
294
- end
295
-
296
- # The `:strict` option mixes in this `Strict` module, which raises rather
297
- # than create `StringReference`s for unknown classes.
298
- module Strict
299
- def _double(klass, type)
300
- ref = if self.class.const_defined?(klass)
301
- type.new(self.class.const_get(klass))
302
- else
303
- raise DoubleFailure, "#{klass} is not a valid class name"
304
- end
305
-
306
- super
307
- end
308
- end
309
-
310
- # An assertion is provided to validate that all expected methods were
311
- # called on a double.
312
- def assert_exhausted(obj)
313
- obj._verify
314
- end
315
-
316
- # Most of the time, `assert_exhausted` will not be called directly, since
317
- # the `:auto_verify` option can be used to call it by default on all
318
- # doubles. That option mixes in this `AutoVerify` module to augment
319
- # methods necessary for this behaviour.
320
- module AutoVerify
321
- def initialize
322
- @doubles = []
323
- end
324
-
325
- def call(unit_of_work)
326
- result = super
327
-
328
- if result.empty?
329
- @doubles.each do |double|
330
- assert_exhausted double
331
- end
332
- end
333
-
334
- result
335
- rescue DoubleFailure => e
336
- [Failure.new(unit_of_work, e.message, e.backtrace)]
337
- end
338
-
339
- def class_double(klass)
340
- x = super
341
- @doubles << x
342
- x
343
- end
344
-
345
- def instance_double(klass)
346
- x = super
347
- @doubles << x
348
- x
349
- end
350
- end
351
- end
352
-
353
- # ### RSpec Integration
354
- #
355
- # This RSpec adapter shows two useful techniques: dynamic library loading
356
- # which removes RSpec as a direct dependency, and use of the `mixin`
357
- # method to further extend the target context.
358
- module RSpecExpectations
359
- def self.included(context)
360
- begin
361
- require 'rspec/expectations'
362
- require 'rspec/matchers'
363
- rescue LoadError
364
- raise "RSpec is not available, cannot use RSpec assertion context."
365
- end
366
-
367
- context.include(RSpec::Matchers)
368
- end
369
-
370
- def call(unit_of_work)
371
- super
372
- rescue RSpec::Expectations::ExpectationNotMetError => e
373
- [Failure.new(unit_of_work, e.message, e.backtrace)]
374
- end
375
- end
376
-
377
- DEFAULT = stack do
378
- include Simple
379
- include Doubles.with(:auto_verify)
380
- end
381
- end
382
- end