/
shared_example_group_spec.rb
353 lines (279 loc) · 13 KB
/
shared_example_group_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
require 'rspec/support/spec/in_sub_process'
module RandomTopLevelModule
def self.setup!
RSpec.shared_examples_for("top level in module") {}
end
end
module RSpec
module Core
RSpec.describe SharedExampleGroup do
include RSpec::Support::InSubProcess
let(:registry) { RSpec.world.shared_example_group_registry }
ExampleModule = Module.new
ExampleClass = Class.new
it 'does not add a bunch of private methods to Module' do
seg_methods = RSpec::Core::SharedExampleGroup.private_instance_methods
expect(Module.private_methods & seg_methods).to eq([])
end
before do
# this is a work around as SharedExampleGroup is not world safe
RandomTopLevelModule.setup!
end
RSpec::Matchers.define :have_example_descriptions do |*descriptions|
match do |example_group|
example_group.examples.map(&:description) == descriptions
end
failure_message do |example_group|
actual = example_group.examples.map(&:description)
"expected #{example_group.name} to have descriptions: #{descriptions.inspect} but had #{actual.inspect}"
end
end
%w[shared_examples shared_examples_for shared_context].each do |shared_method_name|
describe shared_method_name do
let(:group) { RSpec.describe('example group') }
define_method :define_shared_group do |*args, &block|
group.send(shared_method_name, *args, &block)
end
it "is exposed to the global namespace when expose_dsl_globally is enabled" do
in_sub_process do
RSpec.configuration.expose_dsl_globally = true
expect(Kernel).to respond_to(shared_method_name)
end
end
it "is not exposed to the global namespace when monkey patching is disabled" do
RSpec.configuration.expose_dsl_globally = false
expect(Kernel).to_not respond_to(shared_method_name)
end
it "displays a warning when adding a second shared example group with the same name" do
group.send(shared_method_name, 'some shared group') {}
original_declaration = [__FILE__, __LINE__ - 1].join(':')
warning = nil
allow(::Kernel).to receive(:warn) { |msg| warning = msg }
group.send(shared_method_name, 'some shared group') {}
second_declaration = [__FILE__, __LINE__ - 1].join(':')
expect(warning).to include('some shared group', original_declaration, second_declaration)
expect(warning).to_not include 'Called from'
end
it 'works with top level defined examples in modules' do
expect(RSpec::configuration.reporter).to_not receive(:deprecation)
RSpec.describe('example group') { include_context 'top level in module' }
end
it 'generates a named (rather than anonymous) module' do
define_shared_group("shared behaviors", :include_it) { }
example_group = RSpec.describe("Group", :include_it) { }
anonymous_module_regex = /#<Module:0x[0-9a-f]+>/
expect(Module.new.inspect).to match(anonymous_module_regex)
include_a_named_rather_than_anonymous_module = (
include(a_string_including(
"#<RSpec::Core::SharedExampleGroupModule", "shared behaviors"
)).and exclude(a_string_matching(anonymous_module_regex))
)
expect(example_group.ancestors.map(&:inspect)).to include_a_named_rather_than_anonymous_module
expect(example_group.ancestors.map(&:to_s)).to include_a_named_rather_than_anonymous_module
end
["name", :name, ExampleModule, ExampleClass].each do |object|
type = object.class.name.downcase
context "given a #{type}" do
it "captures the given #{type} and block in the collection of shared example groups" do
implementation = lambda { }
define_shared_group(object, &implementation)
expect(registry.find([group], object)).to eq implementation
end
end
end
context "given a hash" do
it "includes itself in matching example groups" do
implementation = Proc.new { def self.bar; 'bar'; end }
define_shared_group(:foo => :bar, &implementation)
matching_group = RSpec.describe "Group", :foo => :bar
non_matching_group = RSpec.describe "Group"
expect(matching_group.bar).to eq("bar")
expect(non_matching_group).not_to respond_to(:bar)
end
end
context "given a string and a hash" do
it "captures the given string and block in the World's collection of shared example groups" do
implementation = lambda { }
define_shared_group("name", :foo => :bar, &implementation)
expect(registry.find([group], "name")).to eq implementation
end
it "delegates include on configuration" do
implementation = Proc.new { def self.bar; 'bar'; end }
define_shared_group("name", :foo => :bar, &implementation)
matching_group = RSpec.describe "Group", :foo => :bar
non_matching_group = RSpec.describe "Group"
expect(matching_group.bar).to eq("bar")
expect(non_matching_group).not_to respond_to(:bar)
end
describe "hooks for individual examples that have matching metadata" do
before do
skip "These specs pass in 2.0 mode on JRuby 1.7.8 but fail on " \
"1.7.15 when the entire spec suite runs. They pass on " \
"1.7.15 when this one spec file is run or if we filter to " \
"just them. Given that 2.0 support on JRuby 1.7 is " \
"experimental, we're just skipping these specs."
end if RUBY_VERSION == "2.0.0" && RSpec::Support::Ruby.jruby?
it 'runs them' do
sequence = []
define_shared_group("name", :include_it) do
before(:context) { sequence << :before_context }
after(:context) { sequence << :after_context }
before(:example) { sequence << :before_example }
after(:example) { sequence << :after_example }
around(:example) do |ex|
sequence << :around_example_before
ex.run
sequence << :around_example_after
end
end
RSpec.describe "group" do
example("ex1") { sequence << :unmatched_example_1 }
example("ex2", :include_it) { sequence << :matched_example }
example("ex3") { sequence << :unmatched_example_2 }
end.run
expect(sequence).to eq([
:unmatched_example_1,
:before_context,
:around_example_before,
:before_example,
:matched_example,
:after_example,
:around_example_after,
:after_context,
:unmatched_example_2
])
end
it 'runs the `after(:context)` hooks even if the `before(:context)` hook raises an error' do
sequence = []
define_shared_group("name", :include_it) do
before(:context) do
sequence << :before_context
raise "boom"
end
after(:context) { sequence << :after_context }
end
RSpec.describe "group" do
example("ex", :include_it) { sequence << :example }
end.run
expect(sequence).to eq([ :before_context, :after_context ])
end
end
end
context "when called at the top level" do
before do
RSpec.__send__(shared_method_name, "shared context") do
example "shared spec"
end
end
it 'is available for inclusion from a top level group' do
group = RSpec.describe "group" do
include_examples "shared context"
end
expect(group).to have_example_descriptions("shared spec")
end
it 'is available for inclusion from a nested example group' do
group = nil
RSpec.describe "parent" do
context "child" do
group = context("grand child") { include_examples "shared context" }
end
end
expect(group).to have_example_descriptions("shared spec")
end
it 'is trumped by a shared group with the same name that is defined in the including context' do
group = RSpec.describe "parent" do
__send__ shared_method_name, "shared context" do
example "a different spec"
end
include_examples "shared context"
end
expect(group).to have_example_descriptions("a different spec")
end
it 'is trumped by a shared group with the same name that is defined in a parent group' do
group = nil
RSpec.describe "parent" do
__send__ shared_method_name, "shared context" do
example "a different spec"
end
group = context("nested") { include_examples "shared context" }
end
expect(group).to have_example_descriptions("a different spec")
end
end
context "when called from within an example group" do
define_method :in_group_with_shared_group_def do |&block|
RSpec.describe "an example group" do
__send__ shared_method_name, "shared context" do
example "shared spec"
end
module_exec(&block)
end
end
it 'is available for inclusion within that group' do
group = in_group_with_shared_group_def do
include_examples "shared context"
end
expect(group).to have_example_descriptions("shared spec")
end
it 'is available for inclusion in a child group' do
group = nil
in_group_with_shared_group_def do
group = context("nested") { include_examples "shared context" }
end
expect(group).to have_example_descriptions("shared spec")
end
it 'is not available for inclusion in a different top level group' do
in_group_with_shared_group_def { }
expect {
RSpec.describe "another top level group" do
include_examples "shared context"
end
}.to raise_error(/Could not find/)
end
it 'is not available for inclusion in a nested group of a different top level group' do
in_group_with_shared_group_def { }
expect {
RSpec.describe "another top level group" do
context("nested") { include_examples "shared context" }
end
}.to raise_error(/Could not find/)
end
it 'trumps a shared group with the same name defined at the top level' do
RSpec.__send__(shared_method_name, "shared context") do
example "a different spec"
end
group = in_group_with_shared_group_def do
include_examples "shared context"
end
expect(group).to have_example_descriptions("shared spec")
end
it 'is trumped by a shared group with the same name that is defined in the including context' do
group = nil
in_group_with_shared_group_def do
group = context "child" do
__send__ shared_method_name, "shared context" do
example "a different spec"
end
include_examples "shared context"
end
end
expect(group).to have_example_descriptions("a different spec")
end
it 'is trumped by a shared group with the same name that is defined in nearer parent group' do
group = nil
in_group_with_shared_group_def do
context "child" do
__send__ shared_method_name, "shared context" do
example "a different spec"
end
group = context("grandchild") { include_examples "shared context" }
end
end
expect(group).to have_example_descriptions("a different spec")
end
end
end
end
end
end
end