Class: Crspec::ExampleGroup

Inherits:
Object
  • Object
show all
Includes:
Expectations, Mock::DSL, SharedDSL
Defined in:
lib/crspec/example_group.rb

Constant Summary collapse

FORBIDDEN_HOOK_SCOPES =
%i[all context suite].freeze

Constants included from Expectations

Crspec::Expectations::ExpectationNotMetError, Crspec::Expectations::UNDEFINED

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SharedDSL

#include_context, #it_behaves_like, #shared_examples

Methods included from Mock::DSL

#allow, #allow_any_instance_of, #double, #expect_any_instance_of, #have_received, #instance_double, #receive, #receive_messages, #spy

Methods included from Mock::ArgumentMatchers

#an_instance_of, #anything, args_match?, #array_including, #duck_type, #hash_excluding, #hash_including, #kind_of, kwargs_match?

Methods included from Expectations

#aggregate_failures, #expect, #is_expected

Methods included from Matchers

#all, #be, #be_a, #be_an_instance_of, #be_falsy, #be_nil, #be_truthy, #be_within, #change, #contain_exactly, #end_with, #eq, #have_attributes, #have_http_status, #match, #match_array, #method_missing, #output, #raise_error, #respond_to, #respond_to_missing?, #satisfy, #start_with, #yield_control, #yield_successive_args, #yield_with_args

Constructor Details

#initialize(description, metadata = {}, parent = nil) ⇒ ExampleGroup

Returns a new instance of ExampleGroup.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/crspec/example_group.rb', line 19

def initialize(description,  = {}, parent = nil)
  @description = description
  @described_module = description.is_a?(Module) ? description : nil
  @metadata = .freeze
  @parent = parent
  @examples = []
  @children = []
  @before_hooks = []
  @after_hooks = []
  @around_hooks = []
  @let_blocks = {}
  @eager_lets = []
  @included_modules = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Crspec::Matchers

Instance Attribute Details

#after_hooksObject (readonly)

Returns the value of attribute after_hooks.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def after_hooks
  @after_hooks
end

#around_hooksObject (readonly)

Returns the value of attribute around_hooks.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def around_hooks
  @around_hooks
end

#before_hooksObject (readonly)

Returns the value of attribute before_hooks.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def before_hooks
  @before_hooks
end

#childrenObject (readonly)

Returns the value of attribute children.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def children
  @children
end

#described_moduleObject

Returns the value of attribute described_module.



17
18
19
# File 'lib/crspec/example_group.rb', line 17

def described_module
  @described_module
end

#descriptionObject (readonly)

Returns the value of attribute description.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def description
  @description
end

#examplesObject (readonly)

Returns the value of attribute examples.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def examples
  @examples
end

#metadataObject (readonly)

Returns the value of attribute metadata.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def 
  @metadata
end

#parentObject (readonly)

Returns the value of attribute parent.



16
17
18
# File 'lib/crspec/example_group.rb', line 16

def parent
  @parent
end

Class Method Details

.define(description, metadata = {}, parent = nil, &block) ⇒ Object



34
35
36
37
38
# File 'lib/crspec/example_group.rb', line 34

def self.define(description,  = {}, parent = nil, &block)
  group = new(description, , parent)
  group.instance_eval(&block) if block
  group
end

Instance Method Details

#after(*args, **kwargs, &block) ⇒ Object



170
171
172
173
# File 'lib/crspec/example_group.rb', line 170

def after(*args, **kwargs, &block)
  reject_serial_scope!(:after, args)
  @after_hooks.unshift({ block: block, filters: args, kwfilters: kwargs })
end

#ancestor_eager_letsObject



238
239
240
241
242
# File 'lib/crspec/example_group.rb', line 238

def ancestor_eager_lets
  @ancestor_eager_lets ||= ancestor_chain.flat_map do |grp|
    grp.instance_variable_get(:@eager_lets)
  end.freeze
end

#ancestor_hooks(type, example = nil) ⇒ Object



216
217
218
# File 'lib/crspec/example_group.rb', line 216

def ancestor_hooks(type, example = nil)
  filtered_hooks(type, example ? effective_type_for(example) : [:type])
end

#ancestor_included_modulesObject



244
245
246
247
248
# File 'lib/crspec/example_group.rb', line 244

def ancestor_included_modules
  @ancestor_included_modules ||= ancestor_chain.flat_map do |grp|
    grp.instance_variable_get(:@included_modules) || []
  end.freeze
end

#ancestor_let_blocksObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/crspec/example_group.rb', line 220

def ancestor_let_blocks
  @ancestor_let_blocks ||= begin
    blocks = {}
    ancestor_chain.each do |grp|
      blocks.merge!(grp.instance_variable_get(:@let_blocks))
    end
    unless blocks.key?(:subject)
      desc_cls = described_class
      if desc_cls.is_a?(Class)
        blocks[:subject] = proc { desc_cls.new rescue nil }
      elsif desc_cls
        blocks[:subject] = proc { desc_cls }
      end
    end
    blocks.freeze
  end
end

#ancestor_metadataObject



250
251
252
253
254
255
256
257
258
# File 'lib/crspec/example_group.rb', line 250

def 
  @ancestor_metadata ||= begin
    meta = {}
    ancestor_chain.each do |grp|
      meta.merge!(grp. || {})
    end
    meta.freeze
  end
end

#around(*args, **kwargs, &block) ⇒ Object



193
194
195
# File 'lib/crspec/example_group.rb', line 193

def around(*args, **kwargs, &block)
  @around_hooks << { block: block, filters: args, kwfilters: kwargs }
end

#attr_accessor(*names) ⇒ Object



140
141
142
143
# File 'lib/crspec/example_group.rb', line 140

def attr_accessor(*names)
  attr_reader(*names)
  attr_writer(*names)
end

#attr_reader(*names) ⇒ Object



128
129
130
131
132
# File 'lib/crspec/example_group.rb', line 128

def attr_reader(*names)
  names.each do |name|
    let(name) { instance_variable_get("@#{name}") }
  end
end

#attr_writer(*names) ⇒ Object



134
135
136
137
138
# File 'lib/crspec/example_group.rb', line 134

def attr_writer(*names)
  names.each do |name|
    define_method("#{name}=") { |val| instance_variable_set("@#{name}", val) }
  end
end

#before(*args, **kwargs, &block) ⇒ Object



165
166
167
168
# File 'lib/crspec/example_group.rb', line 165

def before(*args, **kwargs, &block)
  reject_serial_scope!(:before, args)
  @before_hooks << { block: block, filters: args, kwfilters: kwargs }
end

#create_instance(example) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/crspec/example_group.rb', line 292

def create_instance(example)
  klass = example_class_for(effective_type_for(example))
  inst = klass.new(example)

  klass.setup_callbacks.each do |cb|
    if cb.is_a?(Symbol) || cb.is_a?(String)
      inst.send(cb)
    elsif cb.respond_to?(:call)
      inst.instance_exec(&cb)
    end
  end

  inst
end

#describe(*args, **kwargs, &block) ⇒ Object Also known as: context



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/crspec/example_group.rb', line 40

def describe(*args, **kwargs, &block)
   = kwargs.dup
  .merge!(args.pop) if args.last.is_a?(Hash)
  described = args.first.is_a?(Module) ? args.first : nil
  description = described && args.size == 1 ? described : args.map(&:to_s).join(" ")
  child = self.class.new(description, , self)
  child.described_module = described
  child.instance_eval(&block) if block
  @children << child
  child
end

#described_classObject



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/crspec/example_group.rb', line 264

def described_class
  return @described_class if defined?(@described_class)

  @described_class = begin
    found = nil
    curr = self
    while curr
      if curr.described_module
        found = curr.described_module
        break
      end
      curr = curr.parent
    end
    found
  end
end

#effective_type_for(example) ⇒ Object



281
282
283
284
# File 'lib/crspec/example_group.rb', line 281

def effective_type_for(example)
  meta = example. || {}
  meta.key?(:type) ? meta[:type] : [:type]
end

#fdescribe(*args, **kwargs, &block) ⇒ Object Also known as: fcontext



60
61
62
63
64
# File 'lib/crspec/example_group.rb', line 60

def fdescribe(*args, **kwargs, &block)
  child = describe(*args, **kwargs, &block)
  child.mark_focused!
  child
end

#finalize!Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/crspec/example_group.rb', line 197

def finalize!
  
  ancestor_let_blocks
  ancestor_eager_lets
  ancestor_included_modules
  described_class
  %i[before after around].each { |type| raw_hooks_for(type) }

  example_types = examples.map { |ex| effective_type_for(ex) }.uniq
  example_types.each do |type|
    %i[before after around].each { |hook_type| filtered_hooks(hook_type, type) }
    modules_for_type(type)
    example_class_for(type)
  end

  children.each(&:finalize!)
  self
end

#fit(description = nil, metadata = {}, &block) ⇒ Object



84
85
86
87
88
# File 'lib/crspec/example_group.rb', line 84

def fit(description = nil,  = {}, &block)
  example = it(description, , &block)
  example.focused = true
  example
end

#focused?Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/crspec/example_group.rb', line 108

def focused?
  !!@focused
end

#include(mod) ⇒ Object



112
113
114
# File 'lib/crspec/example_group.rb', line 112

def include(mod)
  @included_modules << mod
end

#it(description = nil, metadata = {}, &block) ⇒ Object Also known as: specify



67
68
69
70
71
72
# File 'lib/crspec/example_group.rb', line 67

def it(description = nil,  = {}, &block)
  example = Example.new(description || "unnamed example", , self, block,
                        pending: pending?)
  @examples << example
  example
end

#let(name, &block) ⇒ Object



145
146
147
# File 'lib/crspec/example_group.rb', line 145

def let(name, &block)
  @let_blocks[name.to_sym] = block
end

#let!(name, &block) ⇒ Object



149
150
151
152
# File 'lib/crspec/example_group.rb', line 149

def let!(name, &block)
  let(name, &block)
  @eager_lets << name.to_sym
end

#mark_focused!Object



101
102
103
104
105
106
# File 'lib/crspec/example_group.rb', line 101

def mark_focused!
  @focused = true
  @examples.each { |ex| ex.focused = true }
  @children.each(&:mark_focused!)
  self
end

#mark_pending!Object



90
91
92
93
94
95
# File 'lib/crspec/example_group.rb', line 90

def mark_pending!
  @pending = true
  @examples.each { |ex| ex.instance_variable_set(:@pending, true) }
  @children.each(&:mark_pending!)
  self
end

#modules_for_example(example) ⇒ Object



260
261
262
# File 'lib/crspec/example_group.rb', line 260

def modules_for_example(example)
  modules_for_type(effective_type_for(example))
end

#pending?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/crspec/example_group.rb', line 97

def pending?
  !!@pending || (parent ? parent.pending? : false)
end

#private(*args) ⇒ Object



119
120
# File 'lib/crspec/example_group.rb', line 119

def private(*args)
end

#protected(*args) ⇒ Object



125
126
# File 'lib/crspec/example_group.rb', line 125

def protected(*args)
end

#public(*args) ⇒ Object



122
123
# File 'lib/crspec/example_group.rb', line 122

def public(*args)
end

#reject_serial_scope!(hook_name, args) ⇒ Object

Raises:



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/crspec/example_group.rb', line 175

def reject_serial_scope!(hook_name, args)
  scope = args.first
  return unless scope.is_a?(Symbol) && FORBIDDEN_HOOK_SCOPES.include?(scope)

  raise MigrationError, <<~MSG
    #{hook_name}(:#{scope}) is not supported by Crspec: shared state across
    examples is inherently serial and breaks the P processes x N threads x
    M fibers execution model.

    Migrate to per-example setup instead:
      - use before(:each) if the setup is cheap
      - use let/let! for lazily-built (memoized per example) values

    Run `crspec-transpile --analyze` on your suite to find and rewrite all
    occurrences automatically.
  MSG
end

#render_views(*args) ⇒ Object



116
117
# File 'lib/crspec/example_group.rb', line 116

def render_views(*args)
end

#run_eager_lets(instance) ⇒ Object



286
287
288
289
290
# File 'lib/crspec/example_group.rb', line 286

def run_eager_lets(instance)
  ancestor_eager_lets.each do |name|
    instance.send(name)
  end
end

#subject(name = nil, &block) ⇒ Object



154
155
156
157
158
159
160
161
# File 'lib/crspec/example_group.rb', line 154

def subject(name = nil, &block)
  if name
    let(name, &block)
    let(:subject) { send(name) }
  else
    let(:subject, &block)
  end
end

#xdescribe(*args, **kwargs, &block) ⇒ Object Also known as: xcontext



53
54
55
56
57
# File 'lib/crspec/example_group.rb', line 53

def xdescribe(*args, **kwargs, &block)
  child = describe(*args, **kwargs, &block)
  child.mark_pending!
  child
end

#xit(description = nil, metadata = {}, &block) ⇒ Object Also known as: pending, skip



75
76
77
78
79
80
# File 'lib/crspec/example_group.rb', line 75

def xit(description = nil,  = {}, &block)
  example = Example.new(description || "unnamed example", , self, block,
                        pending: true)
  @examples << example
  example
end