calificador 0.1.0 → 0.2.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 +4 -4
- data/README.md +35 -16
- data/TODO.md +16 -0
- data/calificador.gemspec +54 -0
- data/lib/calificador.rb +8 -4
- data/lib/calificador/assert.rb +15 -0
- data/lib/calificador/assertor.rb +79 -35
- data/lib/calificador/build/attribute_evaluator.rb +34 -30
- data/lib/calificador/build/basic_factory.rb +195 -0
- data/lib/calificador/build/mock_factory.rb +151 -0
- data/lib/calificador/build/object_factory.rb +85 -0
- data/lib/calificador/build/trait.rb +0 -20
- data/lib/calificador/context/basic_context.rb +406 -0
- data/lib/calificador/context/class_method_context.rb +0 -0
- data/lib/calificador/{spec → context}/condition_context.rb +1 -3
- data/lib/calificador/{spec/type_context.rb → context/instance_context.rb} +5 -10
- data/lib/calificador/context/operation_context.rb +27 -0
- data/lib/calificador/context/override/argument_override.rb +73 -0
- data/lib/calificador/context/override/basic_override.rb +14 -0
- data/lib/calificador/context/override/factory_override.rb +31 -0
- data/lib/calificador/context/override/property_override.rb +61 -0
- data/lib/calificador/context/test_environment.rb +283 -0
- data/lib/calificador/{spec → context}/test_method.rb +2 -31
- data/lib/calificador/{spec → context}/test_root.rb +3 -15
- data/lib/calificador/{spec/examine_context.rb → context/type_context.rb} +7 -10
- data/lib/calificador/key.rb +27 -15
- data/lib/calificador/minitest/minitest_patches.rb +0 -2
- data/lib/calificador/test.rb +1 -3
- data/lib/calificador/test_mixin.rb +143 -139
- data/lib/calificador/util/call_formatter.rb +5 -5
- data/lib/calificador/util/core_extensions.rb +104 -79
- data/lib/calificador/util/proxy_object.rb +63 -0
- data/lib/calificador/version.rb +1 -1
- metadata +22 -42
- data/lib/calificador/build/attribute_container.rb +0 -103
- data/lib/calificador/build/factory.rb +0 -132
- data/lib/calificador/spec/basic_context.rb +0 -353
- data/lib/calificador/spec/class_method_context.rb +0 -42
- data/lib/calificador/spec/instance_method_context.rb +0 -38
- data/lib/calificador/spec/test_environment.rb +0 -141
- data/lib/calificador/spec/value_override.rb +0 -37
@@ -0,0 +1,406 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
require "ostruct"
|
5
|
+
|
6
|
+
module Calificador
|
7
|
+
module Context
|
8
|
+
class BasicContext
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def extract_arguments(subject_key:, values:, names:)
|
13
|
+
arguments = OpenStruct.new
|
14
|
+
arguments.overrides = [] if names.include?(:overrides)
|
15
|
+
arguments.subject_key = subject_key
|
16
|
+
|
17
|
+
values.each_with_index do |value, value_index|
|
18
|
+
name_index = names.index do |name|
|
19
|
+
case name
|
20
|
+
when :type
|
21
|
+
value.is_a?(Module)
|
22
|
+
when :trait
|
23
|
+
value.is_a?(Symbol)
|
24
|
+
when :name
|
25
|
+
value.is_a?(Symbol)
|
26
|
+
when :description
|
27
|
+
value.is_a?(String)
|
28
|
+
when :init
|
29
|
+
value.is_a?(Proc)
|
30
|
+
when :overrides
|
31
|
+
value.is_a?(Override::BasicOverride)
|
32
|
+
else
|
33
|
+
raise ArgumentError, "Unknown option '#{name}'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
unless name_index
|
38
|
+
raise ArgumentError, "Illegal argument at position #{value_index} for (#{values.join(", ")})"
|
39
|
+
end
|
40
|
+
|
41
|
+
name = names[name_index]
|
42
|
+
|
43
|
+
case name
|
44
|
+
when :type
|
45
|
+
arguments.subject_key = Key[value, arguments.subject_key.trait]
|
46
|
+
when :trait
|
47
|
+
arguments.subject_key = Key[arguments.subject_key.type, value]
|
48
|
+
when :overrides
|
49
|
+
arguments.overrides << value
|
50
|
+
when :init
|
51
|
+
arguments.overrides << Override::FactoryOverride.new(key: arguments.subject_key, function: value)
|
52
|
+
else
|
53
|
+
arguments[name] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
if name == :overrides
|
57
|
+
names.shift(name_index)
|
58
|
+
else
|
59
|
+
names.shift(name_index + 1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
arguments
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_reader :description, :parent, :call_context, :operation_arguments
|
68
|
+
|
69
|
+
def initialize(parent:, subject_key:, description:, overrides: [])
|
70
|
+
raise ArgumentError, "Parent must be a #{BasicContext}" unless parent.nil? || parent.is_a?(BasicContext)
|
71
|
+
raise ArgumentError, "Subject key must be a #{Key}" unless subject_key.is_a?(Key)
|
72
|
+
|
73
|
+
@parent = parent
|
74
|
+
@subject_key = subject_key
|
75
|
+
@description = description
|
76
|
+
|
77
|
+
@children = []
|
78
|
+
@factories = {}
|
79
|
+
@named_factories = {}
|
80
|
+
|
81
|
+
@operation_name = nil
|
82
|
+
@operation_arguments = parent&.operation_arguments.dup || {}
|
83
|
+
|
84
|
+
overrides.map do |override|
|
85
|
+
check_override(value: override)
|
86
|
+
end.map do |override|
|
87
|
+
override.apply(context: self)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def setup; end
|
92
|
+
|
93
|
+
def subtree_root?
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_context(context, &block)
|
98
|
+
@children << context
|
99
|
+
|
100
|
+
context.setup
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_class
|
104
|
+
@test_class ||= (@parent&.test_class || raise(StandardError, "No parent context defines a test class"))
|
105
|
+
end
|
106
|
+
|
107
|
+
def subject_key
|
108
|
+
@subject_key ||= begin
|
109
|
+
@parent&.subject_key || raise(StandardError, "No parent context defines a subject class")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def context_path(subtree: true)
|
114
|
+
add_context_to_path([], subtree: subtree).freeze
|
115
|
+
end
|
116
|
+
|
117
|
+
def full_description
|
118
|
+
context_path.reduce(StringIO.new) do |description, context|
|
119
|
+
description << " " if description.length.positive? && context.separate_description_by_space?
|
120
|
+
description << context.description
|
121
|
+
end.string
|
122
|
+
end
|
123
|
+
|
124
|
+
def root
|
125
|
+
@parent&.root || self
|
126
|
+
end
|
127
|
+
|
128
|
+
def operation_name
|
129
|
+
@operation_name ||= Util::Nil[@parent&.operation_name]
|
130
|
+
@operation_name.unmask_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def add_factory(factory)
|
134
|
+
raise KeyError, "Factory for type #{factory.key.type} already defined" if @factories.key?(factory.key.type)
|
135
|
+
raise KeyError, "Factory with name #{factory.name} already defined" if @named_factories.key?(factory.name)
|
136
|
+
|
137
|
+
@factories[factory.key] = factory
|
138
|
+
@named_factories[factory.name] = factory
|
139
|
+
end
|
140
|
+
|
141
|
+
def factories
|
142
|
+
@factories.dup.freeze
|
143
|
+
end
|
144
|
+
|
145
|
+
def named_factories
|
146
|
+
@named_factories.dup.freeze
|
147
|
+
end
|
148
|
+
|
149
|
+
def lookup_factory(key:, inherited: true)
|
150
|
+
@factories[key] || (@parent&.lookup_factory(key: key) if inherited)
|
151
|
+
end
|
152
|
+
|
153
|
+
def lookup_named_factory(name:)
|
154
|
+
@named_factories[name] || @parent&.lookup_named_factory(name: name)
|
155
|
+
end
|
156
|
+
|
157
|
+
def override_factory(key:)
|
158
|
+
lookup_factory(key: key, inherited: false) || begin
|
159
|
+
parent_factory = @parent&.lookup_factory(key: key)
|
160
|
+
|
161
|
+
factory = Build::ObjectFactory.new(
|
162
|
+
parent: parent_factory,
|
163
|
+
context: self,
|
164
|
+
key: key,
|
165
|
+
name: parent_factory&.name || test_class.__default_factory_name(subject_key: key),
|
166
|
+
source_location: Kernel.caller_locations.first { |l| !l.first.start_with(Calificador::BASE_DIR.to_s) }
|
167
|
+
)
|
168
|
+
|
169
|
+
add_factory(factory)
|
170
|
+
factory
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def arguments(&block)
|
175
|
+
Override::ArgumentOverride.new.config(&block)
|
176
|
+
end
|
177
|
+
|
178
|
+
def_delegator :self, :arguments, :args
|
179
|
+
|
180
|
+
def properties(type = nil, trait = Key::DEFAULT_TRAIT, &block)
|
181
|
+
key = Key[type || subject_key.type, trait]
|
182
|
+
|
183
|
+
Override::PropertyOverride.new(key: key).config(&block)
|
184
|
+
end
|
185
|
+
|
186
|
+
def_delegator :self, :properties, :props
|
187
|
+
|
188
|
+
def factory(type, *description_or_name, &block)
|
189
|
+
arguments = BasicContext.extract_arguments(
|
190
|
+
subject_key: Key[type],
|
191
|
+
values: description_or_name,
|
192
|
+
names: %i[description name]
|
193
|
+
)
|
194
|
+
|
195
|
+
arguments.name ||= test_class.__default_factory_name(subject_key: arguments.subject_key)
|
196
|
+
arguments.description ||= test_class.__default_instance_description(subject_key: arguments.subject_key)
|
197
|
+
|
198
|
+
factory = Build::ObjectFactory.new(
|
199
|
+
context: self,
|
200
|
+
key: arguments.subject_key,
|
201
|
+
name: arguments.name,
|
202
|
+
description: arguments.description,
|
203
|
+
source_location: block&.source_location
|
204
|
+
)
|
205
|
+
|
206
|
+
factory.dsl.instance_exec(&block) unless block.nil?
|
207
|
+
|
208
|
+
add_factory(factory)
|
209
|
+
end
|
210
|
+
|
211
|
+
def mock(type, *description_or_name, &block)
|
212
|
+
arguments = BasicContext.extract_arguments(
|
213
|
+
subject_key: Key[type],
|
214
|
+
values: description_or_name,
|
215
|
+
names: %i[description name]
|
216
|
+
)
|
217
|
+
|
218
|
+
arguments.name ||= test_class.__default_factory_name(subject_key: arguments.subject_key)
|
219
|
+
arguments.description ||= test_class.__default_instance_description(subject_key: arguments.subject_key)
|
220
|
+
|
221
|
+
mock = Build::MockFactory.new(
|
222
|
+
context: self,
|
223
|
+
key: arguments.subject_key,
|
224
|
+
name: arguments.name,
|
225
|
+
description: arguments.description,
|
226
|
+
source_location: block&.source_location
|
227
|
+
)
|
228
|
+
|
229
|
+
mock.dsl.instance_exec(&block) unless block.nil?
|
230
|
+
|
231
|
+
add_factory(mock)
|
232
|
+
end
|
233
|
+
|
234
|
+
def type(*type_or_description_or_overrides, &block)
|
235
|
+
arguments = BasicContext.extract_arguments(
|
236
|
+
subject_key: subject_key,
|
237
|
+
values: type_or_description_or_overrides,
|
238
|
+
names: %i[type description overrides]
|
239
|
+
)
|
240
|
+
|
241
|
+
arguments.description ||= test_class.__default_type_description(subject_key: arguments.subject_key)
|
242
|
+
|
243
|
+
context = Context::TypeContext.new(
|
244
|
+
parent: self,
|
245
|
+
subject_key: arguments.subject_key,
|
246
|
+
description: arguments.description,
|
247
|
+
overrides: arguments.overrides
|
248
|
+
)
|
249
|
+
|
250
|
+
context.configure(block: block)
|
251
|
+
|
252
|
+
add_context(context, &block)
|
253
|
+
end
|
254
|
+
|
255
|
+
def examine(*type_or_trait_or_description_or_init_or_overrides, &block)
|
256
|
+
arguments = BasicContext.extract_arguments(
|
257
|
+
subject_key: subject_key,
|
258
|
+
values: type_or_trait_or_description_or_init_or_overrides,
|
259
|
+
names: %i[type trait description init overrides]
|
260
|
+
)
|
261
|
+
|
262
|
+
arguments.description ||= arguments.subject_key.to_s(base_module: test_class)
|
263
|
+
|
264
|
+
context = Context::InstanceContext.new(
|
265
|
+
parent: self,
|
266
|
+
subject_key: arguments.subject_key,
|
267
|
+
description: arguments.description,
|
268
|
+
overrides: arguments.overrides
|
269
|
+
)
|
270
|
+
|
271
|
+
context.configure(block: block)
|
272
|
+
|
273
|
+
add_context(context, &block)
|
274
|
+
end
|
275
|
+
|
276
|
+
def operation(operation, *trait_or_description_or_init_or_overrides, &block)
|
277
|
+
arguments = BasicContext.extract_arguments(
|
278
|
+
subject_key: subject_key,
|
279
|
+
values: trait_or_description_or_init_or_overrides,
|
280
|
+
names: %i[trait description init overrides]
|
281
|
+
)
|
282
|
+
|
283
|
+
arguments.description ||= "\##{operation}"
|
284
|
+
|
285
|
+
context = Context::OperationContext.new(
|
286
|
+
parent: self,
|
287
|
+
subject_key: arguments.subject_key,
|
288
|
+
name: operation,
|
289
|
+
description: arguments.description,
|
290
|
+
overrides: arguments.overrides
|
291
|
+
)
|
292
|
+
|
293
|
+
context.configure(block: block)
|
294
|
+
|
295
|
+
add_context(context, &block)
|
296
|
+
end
|
297
|
+
|
298
|
+
def must(description, *trait_or_init_or_overrides, &block)
|
299
|
+
arguments = BasicContext.extract_arguments(
|
300
|
+
subject_key: subject_key,
|
301
|
+
values: trait_or_init_or_overrides,
|
302
|
+
names: %i[trait init overrides]
|
303
|
+
)
|
304
|
+
|
305
|
+
context = Context::TestMethod.new(
|
306
|
+
parent: self,
|
307
|
+
subject_key: arguments.subject_key,
|
308
|
+
description: "must #{description}",
|
309
|
+
overrides: arguments.overrides,
|
310
|
+
expected_to_fail: false,
|
311
|
+
body: block
|
312
|
+
)
|
313
|
+
|
314
|
+
add_context(context)
|
315
|
+
end
|
316
|
+
|
317
|
+
def must_fail(description, *trait_or_init_or_overrides, &block)
|
318
|
+
arguments = BasicContext.extract_arguments(
|
319
|
+
subject_key: subject_key,
|
320
|
+
values: trait_or_init_or_overrides,
|
321
|
+
names: %i[trait init overrides]
|
322
|
+
)
|
323
|
+
|
324
|
+
context = Context::TestMethod.new(
|
325
|
+
parent: self,
|
326
|
+
subject_key: arguments.subject_key,
|
327
|
+
description: "must fail #{description}",
|
328
|
+
overrides: arguments.overrides,
|
329
|
+
expected_to_fail: true,
|
330
|
+
body: block
|
331
|
+
)
|
332
|
+
|
333
|
+
add_context(context)
|
334
|
+
end
|
335
|
+
|
336
|
+
def with(*description_or_trait_or_init_or_overrides, &block)
|
337
|
+
condition("with", *description_or_trait_or_init_or_overrides, &block)
|
338
|
+
end
|
339
|
+
|
340
|
+
def without(*description_or_trait_or_init_or_overrides, &block)
|
341
|
+
condition("without", *description_or_trait_or_init_or_overrides, &block)
|
342
|
+
end
|
343
|
+
|
344
|
+
def where(*description_or_trait_or_init_or_overrides, &block)
|
345
|
+
condition("where", *description_or_trait_or_init_or_overrides, &block)
|
346
|
+
end
|
347
|
+
|
348
|
+
def create_subject(environment:)
|
349
|
+
raise "No context defines a text subject" unless parent
|
350
|
+
|
351
|
+
parent.create_subject(environment: environment)
|
352
|
+
end
|
353
|
+
|
354
|
+
def merge_operation_arguments(arguments)
|
355
|
+
@operation_arguments.merge!(arguments)
|
356
|
+
end
|
357
|
+
|
358
|
+
protected
|
359
|
+
|
360
|
+
def configure(block:)
|
361
|
+
test_class.__calificador_configure(context: self, block: block)
|
362
|
+
end
|
363
|
+
|
364
|
+
def condition(conjunction, *description_or_trait_or_init_or_overrides, &block)
|
365
|
+
arguments = BasicContext.extract_arguments(
|
366
|
+
subject_key: subject_key,
|
367
|
+
values: description_or_trait_or_init_or_overrides,
|
368
|
+
names: %i[description trait init overrides]
|
369
|
+
)
|
370
|
+
|
371
|
+
arguments.description ||= begin
|
372
|
+
raise ArgumentError, "Please provide a description if you override values" unless arguments.overrides.empty?
|
373
|
+
|
374
|
+
arguments.subject_key.to_s
|
375
|
+
end
|
376
|
+
|
377
|
+
context = Context::ConditionContext.new(
|
378
|
+
parent: self,
|
379
|
+
subject_key: arguments.subject_key,
|
380
|
+
description: [conjunction, arguments.description].compact.join(" "),
|
381
|
+
overrides: arguments.overrides
|
382
|
+
)
|
383
|
+
|
384
|
+
context.configure(block: block)
|
385
|
+
|
386
|
+
add_context(context, &block)
|
387
|
+
end
|
388
|
+
|
389
|
+
def add_context_to_path(path, subtree: true)
|
390
|
+
@parent.add_context_to_path(path, subtree: subtree) unless @parent.nil? || (subtree && subtree_root?)
|
391
|
+
|
392
|
+
path << self
|
393
|
+
end
|
394
|
+
|
395
|
+
def separate_description_by_space?
|
396
|
+
true
|
397
|
+
end
|
398
|
+
|
399
|
+
def check_override(value:)
|
400
|
+
raise ArgumentError, "Illegal override type #{value.class}" unless value.is_a?(Override::BasicOverride)
|
401
|
+
|
402
|
+
value
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
File without changes
|
@@ -1,14 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
using Calificador::Util::CoreExtensions
|
4
|
-
|
5
3
|
module Calificador
|
6
|
-
module
|
7
|
-
class
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(parent:, subject_key:, description:, overrides: {})
|
4
|
+
module Context
|
5
|
+
class InstanceContext < BasicContext
|
6
|
+
def initialize(parent:, subject_key:, description:, overrides: [])
|
12
7
|
super(
|
13
8
|
parent: parent,
|
14
9
|
description: description,
|
@@ -17,8 +12,8 @@ module Calificador
|
|
17
12
|
)
|
18
13
|
end
|
19
14
|
|
20
|
-
def create_subject(environment
|
21
|
-
subject_key
|
15
|
+
def create_subject(environment:)
|
16
|
+
environment.create_object(key: environment.subject_key)
|
22
17
|
end
|
23
18
|
|
24
19
|
def subtree_root?
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calificador
|
4
|
+
module Context
|
5
|
+
# Context that describes an instance method
|
6
|
+
class OperationContext < BasicContext
|
7
|
+
attr_reader :operation_name
|
8
|
+
|
9
|
+
def initialize(parent:, subject_key:, name:, description: nil, overrides: [])
|
10
|
+
raise ArgumentError, "Operation name must not be nil" if name.nil?
|
11
|
+
|
12
|
+
super(
|
13
|
+
parent: parent,
|
14
|
+
subject_key: subject_key,
|
15
|
+
description: description,
|
16
|
+
overrides: overrides
|
17
|
+
)
|
18
|
+
|
19
|
+
@operation_name = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def separate_description_by_space?
|
23
|
+
@parent && !@parent.is_a?(InstanceContext) && !@parent.is_a?(TypeContext)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|