radius-spec 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|