xspec 0.0.2 → 0.1.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.
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