xspec 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43babe397ea759864cca9b9cf9f7b3f85574df64
4
- data.tar.gz: 9bb1e2521e47fd442469f79c15f5ac0bb785a010
3
+ metadata.gz: a5dba5d7629f6d87591f9abe883290f50372dc64
4
+ data.tar.gz: ead15780cdec15272984c1580051e7d7f2eadded
5
5
  SHA512:
6
- metadata.gz: 4a3278a29ac464e56907888124acf872c5151282718286ee141cd57e933e2b28e15d817c81e20b1a5f29199b2097ba962264190d7df44b604ab974ddfed3e76f
7
- data.tar.gz: 652428e1709ecb942d8e858f764489cf5681d40b8b09e4061c9b2814a93f956ddc8df5d48b39fc382b6ec755f455a2c3df8fb4fcf7f93bea7aebdd10b808fb3d
6
+ metadata.gz: b37433a77450176d0cf1ebeae40d5ad24ef28ce8095887126edfd5dfd0040fdcfbf418a1726862a60eb7f275f347d19a371720331b5376145e256bff08374ef5
7
+ data.tar.gz: 963b1b472454aeaeb03aa956a1e38ff3a9b1a9bb4bf78fcd8c9eb06aae26a31d4591ed8e7b9a4fa1c11f43a7413a2d37112d2bd140c182145fcadc8745922b91
data/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  XSpec
2
2
  =====
3
3
 
4
- XSpec is an rspec-inspired testing library that is written in a literate style
5
- and designed to be obvious to use, highly modular, and easy to extend.
6
-
7
- Open up `lib/xspec.rb` and start reading, or use this [nicely formatted
8
- version](http://xaviershay.github.io/xspec/).
4
+ XSpec is an rspec-inspired testing library for Ruby that is written in a
5
+ literate style and designed to be obvious to use, highly modular, and easy to
6
+ extend.
9
7
 
10
8
  Usage
11
9
  -----
12
10
 
11
+ gem install xspec
12
+
13
13
  The default configuration XSpec provides a number of interesting features:
14
14
  assertions, doubles, and rich output.
15
15
 
@@ -40,7 +40,7 @@ Running this with the built-in runner generates some pretty output. You can't
40
40
  see the colors in this README, but trust me they are quite lovely.
41
41
 
42
42
  ```
43
- > bin/xspec example.rb
43
+ > xspec example.rb
44
44
 
45
45
  my application
46
46
  0.000s does math
@@ -61,8 +61,7 @@ my application fails:
61
61
  bin/xspec:19:in `<main>'
62
62
  ```
63
63
 
64
- Customization
65
- -------------
64
+ ### Customization
66
65
 
67
66
  Every aspect of XSpec is customizable, from how tests are scheduled and run all
68
67
  the way through to formatting of output.
@@ -86,56 +85,29 @@ describe '...' do
86
85
  end
87
86
  ```
88
87
 
89
- Of course, you can easily make your own extension classes as well. A runner
90
- that randomly drops tests and reports random durations? Whatever floats your
91
- boat:
92
-
93
- ``` ruby
94
- require 'xspec'
95
-
96
- class UnhelpfulRunner
97
- attr_reader :notifier
98
-
99
- def initialize(opts)
100
- @notifier = opts.fetch(:notifier)
101
- end
102
-
103
- def run(context)
104
- notifier.run_start
105
-
106
- context.nested_units_of_work.each do |x|
107
- next if rand > 0.9
88
+ Of course, you can make your own extension classes as well. For details, see
89
+ the "Configuration" section of the documentation.
108
90
 
109
- notifier.evaluate_start(x)
110
-
111
- errors = x.immediate_parent.execute(x)
112
- duration = rand
113
- result = XSpec::ExecutedUnitOfWork.new(x, errors, duration)
91
+ Documentation
92
+ -------------
114
93
 
115
- notifier.evaluate_finish(result)
116
- end
94
+ There are two major sources of documentation:
117
95
 
118
- notifier.run_finish
119
- end
120
- end
96
+ * [Main API documentation.](https://xaviershay.github.io/xspec/api.html)
97
+ * [Literate source code.](https://xaviershay.github.io/xspec/)
121
98
 
122
- extend XSpec.dsl(
123
- evaluator: UnhelpfulRunner.new(notifier: XSpec::Notifier::DEFAULT)
124
- )
125
-
126
- describe '...' do
127
- # etc etc
128
- end
129
- ```
99
+ It is expected that regular users of XSpec will read both at least once. There
100
+ isn't much to them, and they will give you a useful mental model of how XSpec
101
+ works.
130
102
 
131
103
  Developing
132
104
  ----------
133
105
 
134
106
  Follow the idioms you find in the source, they are somewhat different than
135
- a traditional Ruby project. Bug fixes welcome, features likely to be rejected
136
- since I have a strong opinion of what this library should and should not do.
137
- Talk to me before embarking on anything large. Tests are written in XSpec,
138
- which might do your head in:
107
+ a traditional Ruby project. Bug fixes welcome, though features are likely to be
108
+ rejected since I have a strong opinion of what this library should and should
109
+ not do. Talk to me before embarking on anything large. Tests are written in
110
+ XSpec, which might do your head in:
139
111
 
140
112
  bundle install
141
- bin/test
113
+ bundle exec bin/xspec
data/bin/xspec CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'xspec'
6
+
3
7
  $LOAD_PATH.unshift "spec"
4
8
  $LOAD_PATH.unshift "lib"
5
9
 
@@ -9,8 +13,6 @@ else
9
13
  Dir['spec/**/*_spec.rb']
10
14
  end
11
15
 
12
- require 'xspec'
13
-
14
16
  files.each do |f|
15
17
  load f
16
18
  end
data/lib/xspec.rb CHANGED
@@ -16,13 +16,16 @@ module XSpec
16
16
  options = XSpec.add_defaults(options)
17
17
 
18
18
  Module.new do
19
+ # Each DSL provides a standard set of methods provided by the [DSL
20
+ # module](dsl.html).
19
21
  include DSL
20
22
 
21
- # Each DSL has its own independent context, which is described in detail
22
- # in `data_structures.rb`.
23
+ # In addition, each DSL has its own independent context, which is
24
+ # described in detail in the
25
+ # [`data_structures.rb`](data_structures.html).
23
26
  def __xspec_context
24
- assertion_context = __xspec_opts.fetch(:assertion_context)
25
- @__xspec_context ||= XSpec::Context.root(assertion_context)
27
+ evaluator = __xspec_opts.fetch(:evaluator)
28
+ @__xspec_context ||= XSpec::Context.root(evaluator)
26
29
  end
27
30
 
28
31
  # Some meta-magic is needed to enable the options from local scope above
@@ -31,9 +34,12 @@ module XSpec
31
34
 
32
35
  # `run!` is where the magic happens. Typically called at the end of a
33
36
  # file (or by `autorun!`), this method takes all the data that was
34
- # accumulated by the DSL methods above and runs it through the evaluator.
37
+ # accumulated by the DSL methods above and runs it through the scheduler.
35
38
  def run!
36
- __xspec_opts.fetch(:evaluator).run(__xspec_context)
39
+ notifier = __xspec_opts.fetch(:notifier)
40
+ scheduler = __xspec_opts.fetch(:scheduler)
41
+
42
+ scheduler.run(__xspec_context, notifier)
37
43
  end
38
44
 
39
45
  # It is often convenient to trigger a run after all files have been
@@ -49,13 +55,14 @@ module XSpec
49
55
  module_function :dsl
50
56
  end
51
57
 
52
- # Understanding the data structures used by XSpec will assist you in
53
- # understanding the behavoural components such as the evaluator and notifier.
58
+ # Understanding the [data structures](data_structures.html) used by XSpec will
59
+ # assist you in understanding the behavoural components such as the scheduler
60
+ # and notifier. Read it next.
54
61
  require 'xspec/data_structures'
55
62
 
56
- # To further explore the code base, dive into the defaults file, which
57
- # describes the different sub-components of XSpec that you can use or
58
- # customize.
63
+ # To further explore the code base, dive into the [defaults
64
+ # file](defaults.html), which describes the different sub-components of XSpec
65
+ # that you can use or customize.
59
66
  require 'xspec/defaults'
60
67
 
61
68
  require 'xspec/dsl'
@@ -7,7 +7,7 @@
7
7
  module XSpec
8
8
  # A unit of work, usually created by the `it` DSL method, is a labeled,
9
9
  # indivisible code block that expresses an assertion about a property of the
10
- # system under test. They are run by an evaluator.
10
+ # system under test. They are run by a scheduler.
11
11
  UnitOfWork = Struct.new(:name, :block)
12
12
 
13
13
 
@@ -20,7 +20,7 @@ module XSpec
20
20
  require 'xspec/dsl'
21
21
  class Context
22
22
  class << self
23
- attr_reader :name, :children, :units_of_work, :assertion_context
23
+ attr_reader :name, :children, :units_of_work, :evaluator
24
24
 
25
25
  # A context includes the same DSL methods as the root level module, which
26
26
  # enables the recursive creation.
@@ -29,33 +29,33 @@ module XSpec
29
29
 
30
30
  # Each nested context creates a new class that inherits from the parent.
31
31
  # Methods can be added to this class as per normal, and are correctly
32
- # inherited by children. When it comes time to run tests, the evaluator
32
+ # inherited by children. When it comes time to run tests, the scheduler
33
33
  # will create a new instance of the context (a class) for each test,
34
34
  # making the defined methods available and also ensuring that there is no
35
35
  # state pollution between tests.
36
- def make(name, assertion_context, &block)
36
+ def make(name, evaluator, &block)
37
37
  x = Class.new(self)
38
- x.initialize!(name, assertion_context)
38
+ x.initialize!(name, evaluator)
39
39
  x.class_eval(&block) if block
40
- x.apply_assertion_context!
40
+ x.apply_evaluator!
41
41
  x
42
42
  end
43
43
 
44
44
  # A class cannot have an implicit initializer, but some variable
45
45
  # inititialization is required so the `initialize!` method is called
46
46
  # explicitly when ever a dynamic subclass is created.
47
- def initialize!(name, assertion_context)
47
+ def initialize!(name, evaluator)
48
48
  @children = []
49
49
  @units_of_work = []
50
50
  @name = name
51
- @assertion_context = assertion_context
51
+ @evaluator = evaluator
52
52
  end
53
53
 
54
54
  # The assertion context should be applied after the user has had a chance
55
55
  # to add their own methods. It needs to be last so that users can't
56
56
  # clobber the assertion methods.
57
- def apply_assertion_context!
58
- include(assertion_context)
57
+ def apply_evaluator!
58
+ include(evaluator)
59
59
  end
60
60
 
61
61
  # Executing a unit of work creates a new instance and hands it off to the
@@ -68,14 +68,14 @@ module XSpec
68
68
 
69
69
  # The root context is nothing special, and behaves the same as all the
70
70
  # others.
71
- def root(assertion_context)
72
- make(nil, assertion_context)
71
+ def root(evaluator)
72
+ make(nil, evaluator)
73
73
  end
74
74
 
75
75
  # Child contexts and units of work are typically added by the `describe`
76
76
  # and `it` DSL methods respectively.
77
77
  def add_child_context(name = nil, opts = {}, &block)
78
- self.children << make(name, assertion_context, &block)
78
+ self.children << make(name, evaluator, &block)
79
79
  end
80
80
 
81
81
  def add_unit_of_work(name = nil, opts = {}, &block)
@@ -91,13 +91,13 @@ module XSpec
91
91
  # This is leaky abstraction, since only units of work are copied from
92
92
  # shared contexts. Methods and child contexts are ignored.
93
93
  def create_shared_context(&block)
94
- make(nil, assertion_context, &block)
94
+ make(nil, evaluator, &block)
95
95
  end
96
96
 
97
97
  def copy_into_tree(source_context)
98
98
  target_context = make(
99
99
  source_context.name,
100
- source_context.assertion_context
100
+ source_context.evaluator
101
101
  )
102
102
  source_context.nested_units_of_work.each do |x|
103
103
  target_context.units_of_work << x.unit_of_work
@@ -2,9 +2,9 @@
2
2
 
3
3
  # These are the defaults used by `XSpec.dsl`, but feel free to specify your own
4
4
  # instead. They are set up in such a way that if you can override a component
5
- # down in the bowels without having to provide an entire top level evaluator.
5
+ # down in the bowels without having to provide an entire top level scheduler.
6
+ require 'xspec/schedulers'
6
7
  require 'xspec/evaluators'
7
- require 'xspec/assertion_contexts'
8
8
  require 'xspec/notifiers'
9
9
 
10
10
  module XSpec
@@ -18,12 +18,12 @@ module XSpec
18
18
  # This is a module that is included as the final step in constructing a
19
19
  # context. Allows for different matchers and expectation frameworks to be
20
20
  # used.
21
- options[:assertion_context] ||= AssertionContext::DEFAULT
21
+ options[:evaluator] ||= Evaluator::DEFAULT
22
22
 
23
- # An evaluator is responsible for scheduling units of work and handing them
23
+ # An scheduler is responsible for scheduling units of work and handing them
24
24
  # off to the assertion context. Any logic regarding threads, remote
25
- # execution or the like belongs in an evaluator.
26
- options[:evaluator] ||= Evaluator::DEFAULT.new(options)
25
+ # execution or the like belongs in a scheduler.
26
+ options[:scheduler] ||= Scheduler::DEFAULT
27
27
  options
28
28
  end
29
29
  module_function :add_defaults
data/lib/xspec/dsl.rb CHANGED
@@ -1,6 +1,11 @@
1
+ # # DSL
2
+
1
3
  # Common DSL functions are provided as a module so that they can be used in
2
4
  # both top-level and nested contexts. The method names are modeled after
3
5
  # rspec, and should behave roughly the same.
6
+ #
7
+ # They delegate to method in the [current context](xspec.html#section-5) named
8
+ # in a way that more accurately represents XSpec implementation details.
4
9
  module XSpec
5
10
  module DSL
6
11
  def it(*args, &block)
@@ -1,40 +1,334 @@
1
1
  # # Evaluators
2
2
 
3
- # Evaluators are responsible for collecting all units of work to be run and
4
- # scheduling them.
3
+ # Evaluators are usually composed together into a stack. The final stack has a
4
+ # single API method `call`, which is sent the unit of work to be executed and
5
+ # must return an array of `Failure` objects. It should not allow code-level
6
+ # exceptions to be raised, though should not block system exceptions
7
+ # (`SignalException`, `NoMemoryError`, etc).
5
8
  module XSpec
6
9
  module Evaluator
7
- # The serial evaluator, unsurprisingly, runs all units of works serially in
8
- # a loop. It is about as simple an evaluator as you can imagine. Parents
9
- # are responsible for actually executing the work.
10
- class Serial
11
- def initialize(opts)
12
- @notifier = opts.fetch(:notifier)
13
- @clock = opts.fetch(:clock, ->{ Time.now.to_f })
10
+ # A stack is typically book-ended by the top and bottom evaluators, 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 evaluator executes the unit of work, with no error handling or
21
+ # extra behaviour. By separating this, all other evaluators layered on top
22
+ # of 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 usually be included as the final module in a stack. It is
31
+ # a catch all to make sure all standard exceptions have been handled and do
32
+ # 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 evaluator 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 the following options:
111
+ #
112
+ # * `strict` forbids doubling of classes that have not been loaded. This
113
+ # should generally be enabled when doing a full spec run, and disabled
114
+ # when running specs in isolation.
115
+ #
116
+ # The `with` method returns a module that can be included in a stack.
117
+ def self.with(*opts)
118
+ modules = [self] + opts.map {|x| {
119
+ strict: Strict
120
+ }.fetch(x) }
121
+
122
+
123
+ Module.new do
124
+ modules.each do |m|
125
+ include m
126
+ end
127
+ end
128
+ end
129
+
130
+ # An instance double stands in for an instance of the given class
131
+ # reference, given as a string. The class does not need to be loaded, but
132
+ # if it is then only public instance methods defined on the class are
133
+ # able to be expected.
134
+ def instance_double(klass)
135
+ _double(klass, InstanceReference)
136
+ end
137
+
138
+ # Simarly, a class double validates that class responds to all expected
139
+ # methods, if that class has been loaded.
140
+ def class_double(klass)
141
+ _double(klass, ClassReference)
142
+ end
143
+
144
+ # If the doubled class has not been loaded, a null object reference is
145
+ # used that allows expecting of all methods.
146
+ def _double(klass, type)
147
+ ref = if self.class.const_defined?(klass)
148
+ type.new(self.class.const_get(klass))
149
+ else
150
+ StringReference.new(klass)
151
+ end
152
+
153
+ Double.new(ref)
14
154
  end
15
155
 
16
- def run(context)
17
- notifier.run_start
156
+ # Use `verify` to assert that a method was called on a double with
157
+ # particular arguments. Doubles record all received messages, so `verify`
158
+ # should be called at the end of your test.
159
+ def verify(obj)
160
+ Proxy.new(obj, :_verify)
161
+ end
162
+
163
+
164
+ # All messages sent to a double will return `nil`. Use `stub` to specify
165
+ # a return value instead: `stub(double).some_method(1, 2) { "return
166
+ # value" }`. This must be called before the message is sent to the
167
+ # double.
168
+ def stub(obj)
169
+ Proxy.new(obj, :_stub)
170
+ end
171
+
172
+ # The proxy object captures messages sent to it and passes them through
173
+ # to either the `_verify` of `_stub` method on the double.
174
+ class Proxy < BasicObject
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
+ # message can be recorded and an appropriate return value selected from
188
+ # the stubs.
189
+ class Double < BasicObject
190
+ def initialize(klass)
191
+ @klass = klass
192
+ @expected = []
193
+ @received = []
194
+ end
195
+
196
+ def method_missing(*actual_args)
197
+ stub = @expected.find {|expected_args, ret|
198
+ expected_args == actual_args
199
+ }
200
+
201
+ ret = if stub
202
+ stub[1].call
203
+ end
204
+
205
+ @received << actual_args
206
+
207
+ ret
208
+ end
209
+
210
+ # The two methods needed on this object to set it up and verify it are
211
+ # prefixed by `_` to try to ensure they don't clash with any method
212
+ # expectations. While not fail-safe, users should only be using
213
+ # expectations for a public API, and `_` is traditionally only used
214
+ # for private methods (if at all).
215
+ def _stub(args, &ret)
216
+ @klass.validate_call! args
217
+
218
+ @expected << [args, ret]
219
+ end
220
+
221
+ def _verify(args)
222
+ @klass.validate_call! args
223
+
224
+ i = @received.index(args)
225
+
226
+ if i
227
+ @received.delete_at(i)
228
+ else
229
+ name, rest = *args
230
+ ::Kernel.raise DoubleFailure,
231
+ "Did not receive: %s(%s)\nDid receive:%s\n" % [
232
+ name,
233
+ [*rest].map(&:inspect).join(", "),
234
+ @received.map {|name, *args|
235
+ " %s(%s)" % [name, args.map(&:inspect).join(", ")]
236
+ }.join("\n")
237
+ ]
238
+ end
239
+ end
240
+ end
241
+
242
+ # A reference can be thought of as a "backing object" for a double. It
243
+ # provides an API to validate that a method being expected actually
244
+ # exists - the implementation is different for the different types of
245
+ # doubles.
246
+ class Reference
247
+ def initialize(klass)
248
+ @klass = klass
249
+ end
250
+
251
+ def validate_call!(args)
252
+ end
253
+
254
+ def to_s
255
+ @klass.to_s
256
+ end
257
+ end
18
258
 
19
- context.nested_units_of_work.each do |x|
20
- notifier.evaluate_start(x)
259
+ # A string reference is the "null object" of references, allowing all
260
+ # methods to be expected. It is used when nothing is known about the
261
+ # referenced class (such as when it has not been loaded).
262
+ class StringReference < Reference
263
+ end
21
264
 
22
- start_time = clock.()
23
- errors = x.immediate_parent.execute(x)
24
- finish_time = clock.()
265
+ # Class and Instance references are backed by loaded classes, and
266
+ # restrict the messages that can be expected on a double.
267
+ class ClassReference < Reference
268
+ def validate_call!(args)
269
+ name, rest = *args
25
270
 
26
- result = ExecutedUnitOfWork.new(x, errors, finish_time - start_time)
27
- notifier.evaluate_finish(result)
271
+ unless @klass.respond_to?(name)
272
+ raise DoubleFailure,
273
+ "#{@klass}.#{name} is unimplemented or not public"
274
+ end
28
275
  end
276
+ end
277
+
278
+ class InstanceReference < Reference
279
+ def validate_call!(args)
280
+ name, rest = *args
29
281
 
30
- notifier.run_finish
282
+ unless @klass.public_instance_methods.include?(name)
283
+ raise DoubleFailure,
284
+ "#{@klass}##{name} is unimplemented or not public"
285
+ end
286
+ end
31
287
  end
32
288
 
33
- protected
289
+ # The `:strict` option mixes in this `Strict` module, which raises rather
290
+ # than create a `StringReference` for unknown classes.
291
+ module Strict
292
+ def _double(klass, type)
293
+ ref = if self.class.const_defined?(klass)
294
+ type.new(self.class.const_get(klass))
295
+ else
296
+ raise DoubleFailure, "#{klass} is not a valid class name"
297
+ end
34
298
 
35
- attr_reader :notifier, :clock
299
+ super
300
+ end
301
+ end
36
302
  end
37
303
 
38
- DEFAULT = Serial
304
+
305
+ # ### RSpec Integration
306
+ #
307
+ # This RSpec adapter shows two useful techniques: dynamic library loading
308
+ # which removes RSpec as a direct dependency, and use of the `mixin`
309
+ # method to further extend the target evalutor.
310
+ module RSpecExpectations
311
+ def self.included(mod)
312
+ begin
313
+ require 'rspec/expectations'
314
+ require 'rspec/matchers'
315
+ rescue LoadError
316
+ raise "RSpec is not available, cannot use RSpec assertion context."
317
+ end
318
+
319
+ mod.include(RSpec::Matchers)
320
+ end
321
+
322
+ def call(unit_of_work)
323
+ super
324
+ rescue RSpec::Expectations::ExpectationNotMetError => e
325
+ [Failure.new(unit_of_work, e.message, e.backtrace)]
326
+ end
327
+ end
328
+
329
+ DEFAULT = stack do
330
+ include Simple
331
+ include Doubles
332
+ end
39
333
  end
40
334
  end