radius-spec 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.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +1 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +12 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +11 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +26 -0
- data/LICENSE.txt +201 -0
- data/README.md +474 -0
- data/Rakefile +8 -0
- data/bin/ci +23 -0
- data/bin/console +12 -0
- data/bin/pry +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/bin/travis +29 -0
- data/bin/yard +29 -0
- data/common_rubocop.yml +290 -0
- data/common_rubocop_rails.yml +113 -0
- data/lib/radius/spec/model_factory.rb +472 -0
- data/lib/radius/spec/rails.rb +49 -0
- data/lib/radius/spec/rspec.rb +141 -0
- data/lib/radius/spec/version.rb +7 -0
- data/lib/radius/spec.rb +11 -0
- data/radius-spec.gemspec +32 -0
- metadata +115 -0
@@ -0,0 +1,472 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Radius
|
4
|
+
module Spec
|
5
|
+
# Basic Model Factory
|
6
|
+
#
|
7
|
+
# This factory is **not** Rails specific. It works for any object type that
|
8
|
+
# responds to `new` with a hash of attributes or keywords; including
|
9
|
+
# `Struct` using the new Ruby 2.5 `keyword_init` flag.
|
10
|
+
#
|
11
|
+
# To make this feature available require it after the gem:
|
12
|
+
#
|
13
|
+
# ```ruby
|
14
|
+
# require 'radius/spec'
|
15
|
+
# require 'radius/spec/model_factory'
|
16
|
+
# ```
|
17
|
+
#
|
18
|
+
# ### Storing Factory Templates
|
19
|
+
#
|
20
|
+
# Our convention is to store all of a project's factory templates in the
|
21
|
+
# file `spec/support/model_factories.rb`. As this is our convention, when
|
22
|
+
# the model factory is required it will attempt to load this file
|
23
|
+
# automatically as a convenience.
|
24
|
+
#
|
25
|
+
# ### Including Helpers in Specs
|
26
|
+
#
|
27
|
+
# There are multiple ways you can build object instances using this model
|
28
|
+
# factory. Which method you choose depends on how much perceived
|
29
|
+
# magic/syntactic sugar you want:
|
30
|
+
#
|
31
|
+
# - call the model factory directly
|
32
|
+
# - manually include the factory helper methods in the specs
|
33
|
+
# - use metadata to auto load this feature and include it in the specs
|
34
|
+
#
|
35
|
+
# When using the metadata option you do not need to explicitly require the
|
36
|
+
# model factory feature. This gem registers metadata with the RSpec
|
37
|
+
# configuration when it loads and `RSpec` is defined. When the metadata is
|
38
|
+
# first used it will automatically require the model factory feature and
|
39
|
+
# include the helpers.
|
40
|
+
#
|
41
|
+
# Any of following metadata will include the factory helpers:
|
42
|
+
#
|
43
|
+
# - `:model_factory`
|
44
|
+
# - `:model_factories`
|
45
|
+
# - `type: :controller`
|
46
|
+
# - `type: :feature`
|
47
|
+
# - `type: :job`
|
48
|
+
# - `type: :model`
|
49
|
+
# - `type: :request`
|
50
|
+
# - `type: :system`
|
51
|
+
#
|
52
|
+
# @example defining a single factory template
|
53
|
+
# require 'radius/spec/model_factory'
|
54
|
+
#
|
55
|
+
# Radius::Spec::ModelFactory.define_factory(
|
56
|
+
# "AnyClass",
|
57
|
+
# attr1: :any_value,
|
58
|
+
# attr2: :another_value,
|
59
|
+
# )
|
60
|
+
# @example defining multiple templates
|
61
|
+
# require 'radius/spec/model_factory'
|
62
|
+
#
|
63
|
+
# Radius::Spec::ModelFactory.catalog do |c|
|
64
|
+
# c.factory "AnyClass", attr1: :any_value, attr2: :another_value
|
65
|
+
#
|
66
|
+
# c.factory "AnotherClass",
|
67
|
+
# attr1: :any_value,
|
68
|
+
# attr2: :another_value,
|
69
|
+
# attr3: %i[any list of values]
|
70
|
+
# end
|
71
|
+
# @example building a domain model from a factory template
|
72
|
+
# an_instance = Radius::Spec::ModelFactory.build("AnyClass")
|
73
|
+
# @example building a domain model with custom attributes
|
74
|
+
# an_instance = Radius::Spec::ModelFactory.build(
|
75
|
+
# "AnyClass",
|
76
|
+
# attr1: "Any Custom Value",
|
77
|
+
# attr2: %w[any custom array],
|
78
|
+
# )
|
79
|
+
# @example call the model factory directly in specs
|
80
|
+
# require 'radius/spec/model_factory'
|
81
|
+
#
|
82
|
+
# RSpec.describe AnyClass do
|
83
|
+
# it "includes the factory helpers" do
|
84
|
+
# an_object = Radius::Spec::ModelFactory.build("AnyClass")
|
85
|
+
# expect(an_object.name).to eq "Any Name"
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# @example manually include the factory helper methods
|
89
|
+
# require 'radius/spec/model_factory'
|
90
|
+
#
|
91
|
+
# RSpec.describe AnyClass do
|
92
|
+
# include Radius::Spec::ModelFactory
|
93
|
+
#
|
94
|
+
# it "includes the factory helpers" do
|
95
|
+
# an_object = build(AnyClass)
|
96
|
+
# expect(an_object.name).to eq "Any Name"
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# @example use metadata to auto include the factory helper methods
|
100
|
+
# RSpec.describe AnyClass, :model_factory do
|
101
|
+
# it "includes the factory helpers" do
|
102
|
+
# an_object = build("AnyClass")
|
103
|
+
# expect(an_object.name).to eq "Any Name"
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
# @since 0.1.0
|
107
|
+
module ModelFactory
|
108
|
+
# Indicates that a model does not have a template registered with the
|
109
|
+
# factory {Radius::Spec::ModelFactory.catalog}.
|
110
|
+
class TemplateNotFound < KeyError; end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
# Suggested method for defining multiple factory templates at once.
|
114
|
+
#
|
115
|
+
# Most projects end up having many domain models which need factories
|
116
|
+
# defined. Having to reference the full module constant every time you
|
117
|
+
# want to define a factory is tedious. Use this to define all of your
|
118
|
+
# model templates within a block.
|
119
|
+
#
|
120
|
+
# @example
|
121
|
+
# require 'radius/spec/model_factory'
|
122
|
+
#
|
123
|
+
# Radius::Spec::ModelFactory.catalog do |c|
|
124
|
+
# c.factory "AnyClass", attr1: :any_value, attr2: :another_value
|
125
|
+
#
|
126
|
+
# c.factory "AnotherClass",
|
127
|
+
# attr1: :any_value,
|
128
|
+
# attr2: :another_value,
|
129
|
+
# attr3: %i[any list of values]
|
130
|
+
# end
|
131
|
+
# @yieldparam catalog current catalog storing the registered templates
|
132
|
+
# @return [void]
|
133
|
+
# @see .factory
|
134
|
+
def catalog
|
135
|
+
yield self
|
136
|
+
end
|
137
|
+
|
138
|
+
# Convenience helper for registering a template to the current catalog.
|
139
|
+
#
|
140
|
+
# Registers the `class_name` in the catalog mapped to the provided
|
141
|
+
# `attrs` attribute template.
|
142
|
+
#
|
143
|
+
# ### Lazy Class Loading
|
144
|
+
#
|
145
|
+
# When testing in isolation we often don't want to wait a long time for
|
146
|
+
# a lot of unnecessary project/app code to load. With that in mind we
|
147
|
+
# want to keep loading the model factory and all factory templates as
|
148
|
+
# fast as possible. This mean not loading the associated project/app
|
149
|
+
# code at factory template definition time. This way if you only need
|
150
|
+
# one or two factories your remaining domain model code won't be
|
151
|
+
# loaded.
|
152
|
+
#
|
153
|
+
# Lazy class loading occurs when you register factory template using a
|
154
|
+
# string or symbol for the fully qualified `class_name`. The only
|
155
|
+
# requirement for this feature is that the class must be loaded by the
|
156
|
+
# project/app, or made available via some auto-loading mechanism, by
|
157
|
+
# the time the first instance is built by the factory.
|
158
|
+
#
|
159
|
+
# ### Template Attribute Keys
|
160
|
+
#
|
161
|
+
# Attribute keys may be defined using either strings or symbols.
|
162
|
+
# However, they will be stored internally as symbols. This means that
|
163
|
+
# when an object instance is create using the factory the **attribute
|
164
|
+
# hash will be provided to `new` with _symbol_ keys**.
|
165
|
+
#
|
166
|
+
# ### Dynamic Attribute Values (i.e. Generators)
|
167
|
+
#
|
168
|
+
# Dynamic attributes values may be registered by providing a `Proc` for
|
169
|
+
# the value. For any template attribute which has a `Proc` for a value
|
170
|
+
# making an instance through the factory will send `call` to the proc
|
171
|
+
# with no args.
|
172
|
+
#
|
173
|
+
# <div class="note notetag">
|
174
|
+
# <strong>Note:</strong>
|
175
|
+
# <div class="inline">
|
176
|
+
# <p>
|
177
|
+
# This only applies to template values which are instances of
|
178
|
+
# <code>Proc</code>. If you define a template value using another
|
179
|
+
# object which responds to <code>call</code> that object will be set as
|
180
|
+
# the built instance's attribute value without receiving
|
181
|
+
# <code>call</code>.
|
182
|
+
# </p>
|
183
|
+
# </div>
|
184
|
+
# </div>
|
185
|
+
#
|
186
|
+
# While this is a powerful technique we suggest keeping it's use to a
|
187
|
+
# minimum. There's a lot of benefit to generative, mutation, and fuzzy
|
188
|
+
# testing. We just aren't convinced it should be the default when you
|
189
|
+
# generate unit / general integration test data.
|
190
|
+
#
|
191
|
+
# ### Optional and Required attributes
|
192
|
+
#
|
193
|
+
# Templates may use the special symbols `:optional` and `:required` as
|
194
|
+
# a means of documenting attributes. These special symbols are meant as
|
195
|
+
# descriptive placeholders for developers reading the factory
|
196
|
+
# definition. Any template attribute with a value of `:optional`, which
|
197
|
+
# is not overwritten by a custom value, will be removed just prior to
|
198
|
+
# building a new instance.
|
199
|
+
#
|
200
|
+
# Those attributes marked as `:required` will not be removed. Instead
|
201
|
+
# the symbol `:required` will be set as the attribute's value if it
|
202
|
+
# isn't overwritten by the custom data. This type of value is a _benign
|
203
|
+
# default_ meant to cause errors to provide a more helpful description
|
204
|
+
# (i.e. this attribute is required).
|
205
|
+
#
|
206
|
+
# For Rails projects, we suggest using `:required` for any association
|
207
|
+
# that is necessary for the object to be valid. We do not recommend
|
208
|
+
# attempting to generate default records within the factory as this can
|
209
|
+
# lead to unexpected database state; and hide relevant information away
|
210
|
+
# from the specs which may depend on it.
|
211
|
+
#
|
212
|
+
# ### "Safe" Attribute Duplication
|
213
|
+
#
|
214
|
+
# In an effort to help limit accidental state leak between instances
|
215
|
+
# the factory will duplicate all non-frozen template values prior to
|
216
|
+
# building the instance. Duplication is only applied to the values
|
217
|
+
# registered for the templates. Custom values provided when building
|
218
|
+
# the instance are not duplicated.
|
219
|
+
#
|
220
|
+
# @example register a domain template using class constant
|
221
|
+
# Radius::Spec::ModelFactory.define_factory AnyClass,
|
222
|
+
# any_attr: :any_value
|
223
|
+
# @example register a domain template using lazy class loading
|
224
|
+
# Radius::Spec::ModelFactory.define_factory :AnyClass,
|
225
|
+
# any_attr: :any_value
|
226
|
+
# @example register a nested class using lazy class loading
|
227
|
+
# Radius::Spec::ModelFactory.define_factory "AnyModule::AnyClass",
|
228
|
+
# any_attr: :any_value
|
229
|
+
# @example advanced example using additional features
|
230
|
+
# Radius::Spec::ModelFactory.define_factory(
|
231
|
+
# "AnyClass",
|
232
|
+
# dynamic: -> { rand(0..100) },
|
233
|
+
# safe_array: [1, 2, 3],
|
234
|
+
# )
|
235
|
+
#
|
236
|
+
# AnyClass = Struct.new(:dynamic, :safe_array, keyword_init: true)
|
237
|
+
#
|
238
|
+
# instance_a = Radius::Spec::ModelFactory.build("AnyClass")
|
239
|
+
# # => #<struct AnyClass dynamic=10, safe_array=[1, 2, 3]>
|
240
|
+
#
|
241
|
+
# instance_a.safe_array << 4
|
242
|
+
# # => #<struct AnyClass dynamic=10, safe_array=[1, 2, 3, 4]>
|
243
|
+
#
|
244
|
+
# instance_b = Radius::Spec::ModelFactory.build("AnyClass")
|
245
|
+
# # => #<struct AnyClass dynamic=32, safe_array=[1, 2, 3]>
|
246
|
+
# @param class_name [Class, String, Symbol] fully qualified domain
|
247
|
+
# model class name or constant
|
248
|
+
# @param attrs [Hash{String,Symbol => Object}] hash of attributes and
|
249
|
+
# default values to register
|
250
|
+
# @return [void]
|
251
|
+
def define_factory(class_name, attrs = {})
|
252
|
+
templates[class_name.to_s] = attrs.transform_keys(&:to_sym).freeze
|
253
|
+
end
|
254
|
+
alias_method :factory, :define_factory
|
255
|
+
|
256
|
+
# @private
|
257
|
+
def safe_transform(value)
|
258
|
+
return value.call if value.is_a?(Proc)
|
259
|
+
return value if value.frozen?
|
260
|
+
value.dup
|
261
|
+
end
|
262
|
+
|
263
|
+
# @private
|
264
|
+
def template(name)
|
265
|
+
templates.fetch(name) {
|
266
|
+
raise TemplateNotFound, "template not found: #{name}"
|
267
|
+
}
|
268
|
+
end
|
269
|
+
|
270
|
+
# @private
|
271
|
+
def templates
|
272
|
+
@templates ||= {}
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
module_function
|
277
|
+
|
278
|
+
# Convenience wrapper for building a model template.
|
279
|
+
#
|
280
|
+
# All `custom_attrs` values are provided as is to the class initializer
|
281
|
+
# (i.e. they are **not** duplicate or modified in any way). When an
|
282
|
+
# attribute exists in both the registered template and `custom_attrs` the
|
283
|
+
# value in `custom_attrs` will be used. The `custom_attrs` may also
|
284
|
+
# include new attributes not defined in the factory template.
|
285
|
+
#
|
286
|
+
# ### Optional Block
|
287
|
+
#
|
288
|
+
# The `block` is optional. When provided it is passed directly to `new`
|
289
|
+
# when initializing the instance. This is to support the common Ruby
|
290
|
+
# idiom of yielding `self` within initialize:
|
291
|
+
#
|
292
|
+
# ```ruby
|
293
|
+
# class AnyClass
|
294
|
+
# def initialize(attrs = {})
|
295
|
+
# # setup attrs
|
296
|
+
# yield self if block_given?
|
297
|
+
# end
|
298
|
+
# end
|
299
|
+
# ```
|
300
|
+
#
|
301
|
+
# <div class="note notetag">
|
302
|
+
# <strong>Note:</strong>
|
303
|
+
# <div class="inline">
|
304
|
+
# <p>
|
305
|
+
# Since Ruby always supports passing a block to a method, even if the
|
306
|
+
# method does not use the block, it's possible the block will not run if
|
307
|
+
# the class being instantiated does yield to it.
|
308
|
+
# </p>
|
309
|
+
# <p>
|
310
|
+
# Also, while the common idiom is to <code>yield self</code> classes are
|
311
|
+
# free to yield anything. You need to be aware of how the class normally
|
312
|
+
# behaves when passing a block to <code>new</code>.
|
313
|
+
# </p>
|
314
|
+
# </div>
|
315
|
+
# </div>
|
316
|
+
#
|
317
|
+
# The examples below show different ways of interacting with the
|
318
|
+
# following domain model and registered factory template:
|
319
|
+
#
|
320
|
+
# ```ruby
|
321
|
+
# Radius::Spec::ModelFactory.factory "AnyClass",
|
322
|
+
# simple_attr: "any value",
|
323
|
+
# array_attr: %w[any value],
|
324
|
+
# optional_attr: :optional,
|
325
|
+
# dynamic_attr: -> { rand(0..100) }
|
326
|
+
#
|
327
|
+
# class AnyClass
|
328
|
+
# def initialize(**opts)
|
329
|
+
# opts.each do |k, v|
|
330
|
+
# public_send "#{k}=", v
|
331
|
+
# end
|
332
|
+
# yield self if block_given?
|
333
|
+
# end
|
334
|
+
#
|
335
|
+
# attr_accessor :array_attr, :dynamic_attr, :optional_attr, :simple_attr
|
336
|
+
# end
|
337
|
+
# ```
|
338
|
+
#
|
339
|
+
# @example building the default template using lazy class loading
|
340
|
+
# Radius::Spec::ModelFactory.build("AnyClass")
|
341
|
+
# # => #<AnyClass @array_attr=["any", "value"],
|
342
|
+
# # @dynamic_attr=88,
|
343
|
+
# # @simple_attr="any value">
|
344
|
+
# @example building the default template using class constant
|
345
|
+
# Radius::Spec::ModelFactory.build(AnyClass)
|
346
|
+
# # => #<AnyClass @array_attr=["any", "value"],
|
347
|
+
# # @dynamic_attr=3,
|
348
|
+
# # @simple_attr="any value">
|
349
|
+
# @example building the default template with a block
|
350
|
+
# Radius::Spec::ModelFactory.build("AnyClass") { |instance|
|
351
|
+
# instance.simple_attr = "Block Value"
|
352
|
+
# }
|
353
|
+
# # => #<AnyClass @array_attr=["any", "value"],
|
354
|
+
# # @dynamic_attr=27,
|
355
|
+
# # @simple_attr="Block Value">
|
356
|
+
# @example building an instance with custom attributes
|
357
|
+
# Radius::Spec::ModelFactory.build(
|
358
|
+
# "AnyClass",
|
359
|
+
# simple_attr: "Custom Value",
|
360
|
+
# dynamic_attr: "Static Value",
|
361
|
+
# optional_attr: "Optional Value",
|
362
|
+
# )
|
363
|
+
# # => #<AnyClass @array_attr=["any", "value"],
|
364
|
+
# # @dynamic_attr="Static Value",
|
365
|
+
# # @optional_attr="Optional Value",
|
366
|
+
# # @simple_attr="Custom Value">
|
367
|
+
# @example registered template values are safe from modification
|
368
|
+
# instance_a = Radius::Spec::ModelFactory.build("AnyClass")
|
369
|
+
# instance_b = Radius::Spec::ModelFactory.build("AnyClass")
|
370
|
+
# instance_a.simple_attr.upcase!
|
371
|
+
# instance_a.array_attr << "modified"
|
372
|
+
# puts "#{instance_a.simple_attr}: #{instance_a.array_attr}"
|
373
|
+
# puts "#{instance_b.simple_attr}: #{instance_b.array_attr}"
|
374
|
+
#
|
375
|
+
# # Outputs:
|
376
|
+
# # ANY VALUE: ["any", "value", "modified"]
|
377
|
+
# # any value: ["any", "value"]
|
378
|
+
# @example building instances with custom shared data
|
379
|
+
# shared_array = %w[this is shared]
|
380
|
+
# instance_a = Radius::Spec::ModelFactory.build(
|
381
|
+
# "AnyClass",
|
382
|
+
# array_attr: shared_array,
|
383
|
+
# simple_attr: "Instance A",
|
384
|
+
# )
|
385
|
+
# instance_b = Radius::Spec::ModelFactory.build(
|
386
|
+
# "AnyClass",
|
387
|
+
# array_attr: shared_array,
|
388
|
+
# simple_attr: "Instance B",
|
389
|
+
# )
|
390
|
+
# instance_a.array_attr << "modified"
|
391
|
+
# puts "#{instance_a.simple_attr}: #{instance_a.array_attr}"
|
392
|
+
# puts "#{instance_b.simple_attr}: #{instance_b.array_attr}"
|
393
|
+
#
|
394
|
+
# # Outputs:
|
395
|
+
# # Instance A: ["this", "is", "shared", "modified"]
|
396
|
+
# # Instance B: ["this", "is", "shared", "modified"]
|
397
|
+
# @param name [Class, String, Symbol] fully qualified domain model class
|
398
|
+
# name or constant
|
399
|
+
# @param custom_attrs [Hash{String,Symbol => Object}] hash of custom
|
400
|
+
# attributes to replace registered template default values
|
401
|
+
# @param block optional block which is passed through to `new` when
|
402
|
+
# instantiating `name`
|
403
|
+
# @return instance of `name` instantiated with `custom_attrs` and the
|
404
|
+
# registered template attributes
|
405
|
+
# @raise [TemplateNotFound] when no template is defined for `name`
|
406
|
+
# @see .define_factory
|
407
|
+
def build(name, custom_attrs = {}, &block)
|
408
|
+
name = name.to_s
|
409
|
+
template = ::Radius::Spec::ModelFactory.template(name)
|
410
|
+
template_only = template.keys - custom_attrs.keys
|
411
|
+
attrs = template.slice(*template_only)
|
412
|
+
.delete_if { |_, v| :optional == v }
|
413
|
+
.transform_values! { |v|
|
414
|
+
::Radius::Spec::ModelFactory.safe_transform(v)
|
415
|
+
}
|
416
|
+
.merge(custom_attrs)
|
417
|
+
# TODO: Always yield to the provided block even if new doesn't
|
418
|
+
::Object.const_get(name).new(attrs, &block)
|
419
|
+
end
|
420
|
+
|
421
|
+
# Convenience wrapper for building, and persisting, a model template.
|
422
|
+
#
|
423
|
+
# This is a thin wrapper around `build(name, attrs).tap(&:save!)`. The
|
424
|
+
# persistence message `save!` will only be called on objects which
|
425
|
+
# respond to it.
|
426
|
+
#
|
427
|
+
# ### Avoid for New Code
|
428
|
+
#
|
429
|
+
# It is strongly suggested that you avoid using `create` for new code.
|
430
|
+
# Instead be explicit about when and how objects are persisted. This
|
431
|
+
# allows you to have fine grain control over how your data is setup.
|
432
|
+
#
|
433
|
+
# We suggest that you create instances which need to be persisted before
|
434
|
+
# your specs using the following syntax:
|
435
|
+
#
|
436
|
+
# ```ruby
|
437
|
+
# let(:an_instance) { build("AnyClass") }
|
438
|
+
#
|
439
|
+
# before do
|
440
|
+
# an_instance.save!
|
441
|
+
# end
|
442
|
+
# ```
|
443
|
+
#
|
444
|
+
# Alternatively if you really want for the instance be lazy instantiated,
|
445
|
+
# and persisted, pass the appropriate persistence method as the block:
|
446
|
+
#
|
447
|
+
# ```ruby
|
448
|
+
# let(:an_instance) { build("AnyClass", &:save!) }
|
449
|
+
# ```
|
450
|
+
#
|
451
|
+
# @param (see .build)
|
452
|
+
# @return (see .build)
|
453
|
+
# @raise (see .build)
|
454
|
+
# @see .build
|
455
|
+
# @see .define_factory
|
456
|
+
def create(name, custom_attrs = {}, &block)
|
457
|
+
instance = build(name, custom_attrs, &block)
|
458
|
+
instance.save! if instance.respond_to?(:save!)
|
459
|
+
instance
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
# Try to load the factories defined for the specs
|
466
|
+
# rubocop:disable Lint/HandleExceptions
|
467
|
+
begin
|
468
|
+
require 'support/model_factories'
|
469
|
+
rescue LoadError
|
470
|
+
# Ignore as this is an optional convenience feature
|
471
|
+
end
|
472
|
+
# rubocop:enable Lint/HandleExceptions
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Ensure the gem, and default RSpec config, are already loaded
|
4
|
+
require 'radius/spec'
|
5
|
+
require 'radius/spec/rspec'
|
6
|
+
require 'rspec/rails'
|
7
|
+
|
8
|
+
RSpec.configure do
|
9
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
10
|
+
config.fixture_path = ::Rails.root.join("spec", "fixtures")
|
11
|
+
|
12
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
13
|
+
# examples within a transaction, remove the following line or assign false
|
14
|
+
# instead of true.
|
15
|
+
config.use_transactional_fixtures = true
|
16
|
+
|
17
|
+
# Filter lines from Rails gems in backtraces.
|
18
|
+
config.filter_rails_from_backtrace!
|
19
|
+
# arbitrary gems may also be filtered via:
|
20
|
+
# config.filter_gems_from_backtrace("gem name")
|
21
|
+
|
22
|
+
# Skip system tests by default.
|
23
|
+
#
|
24
|
+
# This is what Rails does by default:
|
25
|
+
#
|
26
|
+
# > By default, running `bin/rails` test won't run your system tests. Make
|
27
|
+
# > sure to run `bin/rails test:system` to actually run them.
|
28
|
+
#
|
29
|
+
# Running system tests often requires additional external dependencies. Not
|
30
|
+
# every project is setup to install all of the system test dependencies by
|
31
|
+
# default. To avoid issues across projects we default to excluding these
|
32
|
+
# specs. Projects are free to overwrite this filter in their custom RSpec
|
33
|
+
# configuration.
|
34
|
+
#
|
35
|
+
# This can be overridden on the command line:
|
36
|
+
#
|
37
|
+
# bin/rspec -t type:system
|
38
|
+
#
|
39
|
+
# See:
|
40
|
+
# - http://guides.rubyonrails.org/v5.1.4/testing.html#implementing-a-system-test
|
41
|
+
# - https://relishapp.com/rspec/rspec-core/v/3-7/docs/filtering/exclusion-filters
|
42
|
+
# - http://rspec.info/documentation/3.7/rspec-core/RSpec/Core/Configuration.html#filter_run_excluding-instance_method
|
43
|
+
config.filter_run_excluding type: "system"
|
44
|
+
|
45
|
+
# Always clear the cache before specs to avoid state leak problems
|
46
|
+
config.before do
|
47
|
+
Rails.cache.clear
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
4
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
5
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
6
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
7
|
+
# a separate helper file that requires the additional dependencies and performs
|
8
|
+
# the additional setup, and require it from the spec files that actually need
|
9
|
+
# it.
|
10
|
+
#
|
11
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
12
|
+
RSpec.configure do |config|
|
13
|
+
# rspec-expectations config goes here. You can use an alternate
|
14
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
15
|
+
# assertions if you prefer.
|
16
|
+
config.expect_with :rspec do |expectations|
|
17
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
18
|
+
# and `failure_message` of custom matchers include text for helper methods
|
19
|
+
# defined using `chain`, e.g.:
|
20
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
21
|
+
# # => "be bigger than 2 and smaller than 4"
|
22
|
+
# ...rather than:
|
23
|
+
# # => "be bigger than 2"
|
24
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
28
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
29
|
+
config.mock_with :rspec do |mocks|
|
30
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
31
|
+
# a real object. This is generally recommended, and will default to
|
32
|
+
# `true` in RSpec 4.
|
33
|
+
mocks.verify_partial_doubles = true
|
34
|
+
mocks.allow_message_expectations_on_nil = false
|
35
|
+
mocks.verify_doubled_constant_names = (
|
36
|
+
ENV.key?("CI") || !config.files_to_run.one?
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
41
|
+
# have no way to turn it off -- the option exists only for backwards
|
42
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
43
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
44
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
45
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
46
|
+
|
47
|
+
# This allows you to limit a spec run to individual examples or groups
|
48
|
+
# you care about by tagging them with `:focus` metadata. When nothing
|
49
|
+
# is tagged with `:focus`, all examples get run. RSpec also provides
|
50
|
+
# aliases for `it`, `describe`, and `context` that include `:focus`
|
51
|
+
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
52
|
+
config.filter_run_when_matching :focus
|
53
|
+
|
54
|
+
# Allows RSpec to persist some state between runs in order to support
|
55
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
56
|
+
# you configure your source control system to ignore this file.
|
57
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
58
|
+
|
59
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
60
|
+
# recommended. For more details, see:
|
61
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
62
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
63
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
64
|
+
config.disable_monkey_patching!
|
65
|
+
|
66
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
67
|
+
# be too noisy due to issues in dependencies.
|
68
|
+
config.warnings = true
|
69
|
+
|
70
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
71
|
+
# file, and it's useful to allow more verbose output when running an
|
72
|
+
# individual spec file.
|
73
|
+
if config.files_to_run.one?
|
74
|
+
# Use the documentation formatter for detailed output,
|
75
|
+
# unless a formatter has already been configured
|
76
|
+
# (e.g. via a command-line flag).
|
77
|
+
config.default_formatter = "doc"
|
78
|
+
end
|
79
|
+
|
80
|
+
# RSpec will exit with code 0 indicating success if no examples are defined.
|
81
|
+
# This option allows you to configure RSpec to exit with code 1 indicating
|
82
|
+
# failure. This is useful in CI environments, as it helps detect when you've
|
83
|
+
# misconfigured RSpec to look for specs in the wrong place or with the wrong
|
84
|
+
# pattern. See http://rspec.info/blog/2017/05/rspec-3-6-has-been-released/#core-configfailifnoexamples
|
85
|
+
config.fail_if_no_examples = true
|
86
|
+
|
87
|
+
# Print the 10 slowest examples and example groups at the
|
88
|
+
# end of the spec run, to help surface which specs are running
|
89
|
+
# particularly slow.
|
90
|
+
config.profile_examples = 10 if ENV["CI"]
|
91
|
+
|
92
|
+
# Run specs in random order to surface order dependencies. If you find an
|
93
|
+
# order dependency and want to debug it, you can fix the order by providing
|
94
|
+
# the seed, which is printed after each run.
|
95
|
+
# --seed 1234
|
96
|
+
config.order = :random
|
97
|
+
|
98
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
99
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
100
|
+
# test failures related to randomization by passing the same `--seed` value
|
101
|
+
# as the one that triggered the failure.
|
102
|
+
Kernel.srand config.seed
|
103
|
+
|
104
|
+
config.when_first_matching_example_defined(
|
105
|
+
:model_factory,
|
106
|
+
:model_factories,
|
107
|
+
) do
|
108
|
+
require 'radius/spec/model_factory'
|
109
|
+
config.include Radius::Spec::ModelFactory, :model_factory, :model_factories
|
110
|
+
end
|
111
|
+
|
112
|
+
config.when_first_matching_example_defined(type: :controller) do
|
113
|
+
require 'radius/spec/model_factory'
|
114
|
+
config.include Radius::Spec::ModelFactory, type: :controller
|
115
|
+
end
|
116
|
+
|
117
|
+
config.when_first_matching_example_defined(type: :feature) do
|
118
|
+
require 'radius/spec/model_factory'
|
119
|
+
config.include Radius::Spec::ModelFactory, type: :feature
|
120
|
+
end
|
121
|
+
|
122
|
+
config.when_first_matching_example_defined(type: :job) do
|
123
|
+
require 'radius/spec/model_factory'
|
124
|
+
config.include Radius::Spec::ModelFactory, type: :job
|
125
|
+
end
|
126
|
+
|
127
|
+
config.when_first_matching_example_defined(type: :model) do
|
128
|
+
require 'radius/spec/model_factory'
|
129
|
+
config.include Radius::Spec::ModelFactory, type: :model
|
130
|
+
end
|
131
|
+
|
132
|
+
config.when_first_matching_example_defined(type: :request) do
|
133
|
+
require 'radius/spec/model_factory'
|
134
|
+
config.include Radius::Spec::ModelFactory, type: :request
|
135
|
+
end
|
136
|
+
|
137
|
+
config.when_first_matching_example_defined(type: :system) do
|
138
|
+
require 'radius/spec/model_factory'
|
139
|
+
config.include Radius::Spec::ModelFactory, type: :system
|
140
|
+
end
|
141
|
+
end
|