calificador 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -16
  3. data/TODO.md +16 -0
  4. data/calificador.gemspec +54 -0
  5. data/lib/calificador.rb +8 -4
  6. data/lib/calificador/assert.rb +15 -0
  7. data/lib/calificador/assertor.rb +79 -35
  8. data/lib/calificador/build/attribute_evaluator.rb +34 -30
  9. data/lib/calificador/build/basic_factory.rb +195 -0
  10. data/lib/calificador/build/mock_factory.rb +151 -0
  11. data/lib/calificador/build/object_factory.rb +85 -0
  12. data/lib/calificador/build/trait.rb +0 -20
  13. data/lib/calificador/context/basic_context.rb +406 -0
  14. data/lib/calificador/context/class_method_context.rb +0 -0
  15. data/lib/calificador/{spec → context}/condition_context.rb +1 -3
  16. data/lib/calificador/{spec/type_context.rb → context/instance_context.rb} +5 -10
  17. data/lib/calificador/context/operation_context.rb +27 -0
  18. data/lib/calificador/context/override/argument_override.rb +73 -0
  19. data/lib/calificador/context/override/basic_override.rb +14 -0
  20. data/lib/calificador/context/override/factory_override.rb +31 -0
  21. data/lib/calificador/context/override/property_override.rb +61 -0
  22. data/lib/calificador/context/test_environment.rb +283 -0
  23. data/lib/calificador/{spec → context}/test_method.rb +2 -31
  24. data/lib/calificador/{spec → context}/test_root.rb +3 -15
  25. data/lib/calificador/{spec/examine_context.rb → context/type_context.rb} +7 -10
  26. data/lib/calificador/key.rb +27 -15
  27. data/lib/calificador/minitest/minitest_patches.rb +0 -2
  28. data/lib/calificador/test.rb +1 -3
  29. data/lib/calificador/test_mixin.rb +143 -139
  30. data/lib/calificador/util/call_formatter.rb +5 -5
  31. data/lib/calificador/util/core_extensions.rb +104 -79
  32. data/lib/calificador/util/proxy_object.rb +63 -0
  33. data/lib/calificador/version.rb +1 -1
  34. metadata +22 -42
  35. data/lib/calificador/build/attribute_container.rb +0 -103
  36. data/lib/calificador/build/factory.rb +0 -132
  37. data/lib/calificador/spec/basic_context.rb +0 -353
  38. data/lib/calificador/spec/class_method_context.rb +0 -42
  39. data/lib/calificador/spec/instance_method_context.rb +0 -38
  40. data/lib/calificador/spec/test_environment.rb +0 -141
  41. data/lib/calificador/spec/value_override.rb +0 -37
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- using Calificador::Util::CoreExtensions
4
-
5
- module Calificador
6
- module Build
7
- # Factory calss
8
- class Factory < AttributeContainer
9
- # Configuration derived factories
10
- class Dsl < AttributeContainer::Dsl
11
- def trait(name, description = nil, &block)
12
- factory = Build::Factory.new(
13
- context: @delegate.context,
14
- parent: @delegate,
15
- key: @delegate.key.with(name),
16
- name: [@delegate.name, name].compact.join("_"),
17
- source_location: block.source_location,
18
- description: description || __default_trait_description(trait: name)
19
- )
20
-
21
- factory.dsl_config(&block)
22
- @delegate.context.add_factory(factory)
23
- end
24
-
25
- protected
26
-
27
- def __default_trait_description(trait:)
28
- trait.to_s.gsub("_", " ").chomp
29
- end
30
- end
31
-
32
- attr_reader :key, :context, :name, :source_location
33
- attr_accessor :init_with
34
-
35
- def initialize(context:, parent: nil, key:, name:, description: nil, source_location:, values: nil)
36
- super(parent: parent, description: description)
37
-
38
- raise "Parent factory must have same type" unless parent.nil? || parent.key.type == key.type
39
-
40
- @context = context
41
- @key = key
42
- @name = name.to_sym
43
- @source_location = source_location
44
- @values = values&.dup || {}
45
- end
46
-
47
- def create(context:)
48
- evaluator = AttributeEvaluator.new(context: context)
49
-
50
- collect_attributes_and_values(evaluator: evaluator)
51
-
52
- before_create(evaluator: evaluator)
53
-
54
- object = create_object(evaluator: evaluator)
55
-
56
- set_properties(object: object, evaluator: evaluator)
57
-
58
- after_create(evaluator: evaluator, object: object)
59
-
60
- object
61
- end
62
-
63
- def add_values(values)
64
- @values.merge!(values)
65
- end
66
-
67
- def setup(test_class:); end
68
-
69
- protected
70
-
71
- def collect_attributes_and_values(evaluator:)
72
- @parent&.collect_attributes_and_values(evaluator: evaluator)
73
- evaluator.add_attributes(@attributes.values)
74
- evaluator.add_values(@values)
75
- end
76
-
77
- def before_create(evaluator:)
78
- @parent&.before_create(evaluator: evaluator)
79
- evaluator.evaluate(&@before_create) unless @before_create.nil?
80
- end
81
-
82
- def after_create(evaluator:, object:)
83
- @parent&.after_create(evaluator: evaluator, object: object)
84
- evaluator.evaluate(object, &@after_create) unless @after_create.nil?
85
- end
86
-
87
- def nearest_init_with
88
- @init_with || @parent&.nearest_init_with
89
- end
90
-
91
- def create_object(evaluator:)
92
- init_with = nearest_init_with
93
-
94
- if init_with.nil?
95
- call_initializer(evaluator: evaluator)
96
- else
97
- evaluator.evaluate(&init_with)
98
- end
99
- end
100
-
101
- def set_properties(object:, evaluator:)
102
- evaluator.attributes.each_value do |attribute|
103
- object.send(:"#{attribute.name}=", evaluator.value(name: attribute.name)) if attribute.type == :property
104
- end
105
-
106
- evaluator.values.each do |name, value| # rubocop:disable Style/HashEachMethods
107
- object.send(:"#{name}=", value) unless evaluator.attribute?(name: name)
108
- end
109
- end
110
-
111
- def call_initializer(evaluator:)
112
- parameters = []
113
- options = {}
114
-
115
- @key.type.instance_method(:initialize).parameters.each do |type, name|
116
- case type
117
- when :req
118
- parameters << evaluator.value(name: name)
119
- when :opt
120
- parameters << evaluator.value(name: name) if evaluator.attribute?(name: name)
121
- when :keyreq
122
- options[name] = evaluator.value(name: name)
123
- when :key
124
- options[name] = evaluator.value(name: name) if evaluator.attribute?(name: name)
125
- end
126
- end
127
-
128
- @key.type.new(*parameters, **options)
129
- end
130
- end
131
- end
132
- end
@@ -1,353 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- using Calificador::Util::CoreExtensions
4
-
5
- module Calificador
6
- module Spec
7
- class BasicContext
8
- class Dsl
9
- def initialize(delegate:)
10
- @delegate = delegate
11
- end
12
-
13
- def factory(type, description = nil, name: nil, &block)
14
- key = Key[type]
15
- name ||= BasicContext.default_factory_name(test_class: @delegate.test_class, key: key)
16
-
17
- factory = Build::Factory.new(
18
- context: @delegate,
19
- key: key,
20
- name: name,
21
- description: description,
22
- source_location: block.source_location
23
- )
24
-
25
- factory.dsl_config(&block)
26
- @delegate.add_factory(factory)
27
- end
28
-
29
- def examine(subject_type, description = nil, trait: Key::DEFAULT_TRAIT, **values, &block)
30
- subject_key = Key[subject_type, trait == Key::INHERITED_TRAIT ? Key::DEFAULT_TRAIT : trait]
31
- overrides = BasicContext.collect_overrides(values: values, default_key: subject_key)
32
-
33
- description ||= BasicContext.default_examine_description(
34
- test_class: @delegate.test_class,
35
- subject_key: subject_key,
36
- values: values
37
- )
38
-
39
- context = Spec::ExamineContext.new(
40
- parent: @delegate,
41
- subject_key: subject_key,
42
- description: description,
43
- overrides: overrides
44
- )
45
-
46
- context.dsl_config(&block)
47
-
48
- @delegate.add_context(context, &block)
49
- end
50
-
51
- def method(method, description = nil, &block)
52
- context = Spec::InstanceMethodContext.new(
53
- parent: @delegate,
54
- method: method,
55
- description: description || "\##{method}"
56
- )
57
-
58
- context.dsl_config(&block)
59
-
60
- @delegate.add_context(context, &block)
61
- end
62
-
63
- def class_method(method, description = nil, &block)
64
- context = Spec::ClassMethodContext.new(
65
- parent: @delegate,
66
- method: method,
67
- description: description || ".#{method}"
68
- )
69
-
70
- context.dsl_config(&block)
71
-
72
- @delegate.add_context(context, &block)
73
- end
74
-
75
- def must(description, trait: Key::INHERITED_TRAIT, **values, &block)
76
- subject_key = @delegate.subject_key.with(trait)
77
- overrides = BasicContext.collect_overrides(values: values, default_key: subject_key)
78
-
79
- context = Spec::TestMethod.new(
80
- parent: @delegate,
81
- subject_key: subject_key,
82
- description: "must #{description}",
83
- overrides: overrides,
84
- expected_to_fail: false,
85
- body: block
86
- )
87
-
88
- @delegate.add_context(context)
89
- end
90
-
91
- def must_fail(description, trait:, **values, &block)
92
- subject_key = @delegate.subject_key.with(trait)
93
- overrides = BasicContext.collect_overrides(values: values, default_key: subject_key)
94
-
95
- context = Spec::TestMethod.new(
96
- parent: @delegate,
97
- subject_key: subject_key,
98
- description: "must fail #{description}",
99
- overrides: overrides,
100
- expected_to_fail: true,
101
- body: block
102
- )
103
-
104
- @delegate.add_context(context)
105
- end
106
-
107
- def with(description, trait: Key::INHERITED_TRAIT, **values, &block)
108
- __condition(conjunction: "with", description: description, trait: trait, values: values, &block)
109
- end
110
-
111
- def without(description, trait: Key::INHERITED_TRAIT, **values, &block)
112
- __condition(conjunction: "without", description: description, trait: trait, values: values, &block)
113
- end
114
-
115
- def where(description, trait: Key::INHERITED_TRAIT, **values, &block)
116
- __condition(conjunction: "where", description: "description", trait: trait, values: values, &block)
117
- end
118
-
119
- protected
120
-
121
- def __condition(conjunction:, description:, trait: Key::INHERITED_TRAIT, values: {}, &block)
122
- if description.is_a?(Symbol) && trait == Key::INHERITED_TRAIT
123
- trait = description
124
- description = nil
125
- end
126
-
127
- subject_key = @delegate.subject_key.with(trait)
128
- overrides = BasicContext.collect_overrides(values: values, default_key: subject_key)
129
-
130
- description ||= __default_description(key: subject_key)
131
-
132
- context = Spec::ConditionContext.new(
133
- parent: @delegate,
134
- subject_key: subject_key,
135
- description: description,
136
- overrides: overrides
137
- )
138
-
139
- context.dsl_config(&block)
140
-
141
- @delegate.add_context(context, &block)
142
- end
143
-
144
- def __default_description(key:)
145
- raise "Please provide a description or a trait" if key.trait == Key::INHERITED_TRAIT
146
-
147
- factory = @delegate.lookup_factory(key: key)
148
-
149
- raise "No factory defined for #{key}" if factory.nil?
150
-
151
- factory.description
152
- end
153
- end
154
-
155
- class << self
156
- def default_factory_name(test_class:, key:)
157
- name = key.type.name.delete_prefix(test_class.parent_prefix).gsub("::", "").snake_case
158
-
159
- name = "#{name}_#{key.trait}" if key.trait?
160
-
161
- name.to_sym
162
- end
163
-
164
- def default_examine_description(test_class:, subject_key:, values:)
165
- description = StringIO.new
166
- description << subject_key.type.name.delete_prefix(test_class.parent_prefix)
167
-
168
- description << " (" << subject_key.trait.to_s.gsub("_", " ") << ")" if subject_key.trait?
169
-
170
- append_values_description(description: description, values: values)
171
-
172
- description.string
173
- end
174
-
175
- def collect_overrides(values:, default_key:)
176
- result = Hash.new { |hash, key| hash[key] = ValueOverride.new(key: key) }
177
- default_values = {}
178
-
179
- values.each do |key, value|
180
- case key
181
- when Symbol
182
- default_values[key] = value
183
- when Class
184
- key = Key[key]
185
- result[key] = result[key].merge_values(value)
186
- when Key
187
- result[key] = result[key].merge_values(value)
188
- else
189
- raise ArgumentError, "Illegal override key '#{key}'"
190
- end
191
- end
192
-
193
- result[default_key] = result[default_key].merge_values(default_values) unless default_values.empty?
194
- result.default_proc = nil
195
-
196
- result
197
- end
198
-
199
- protected
200
-
201
- def append_values_description(description:, values:)
202
- unless values.empty?
203
- values.each_with_index do |(name, value), index|
204
- description << case index
205
- when 0
206
- " where "
207
- when values.size - 1
208
- " and "
209
- else
210
- ", "
211
- end
212
-
213
- description << name
214
-
215
- description << case value
216
- when NilClass
217
- "is not set"
218
- when TrueClass
219
- "is true"
220
- when FalseClass
221
- "is false"
222
- else
223
- "is set"
224
- end
225
- end
226
- end
227
-
228
- description
229
- end
230
- end
231
-
232
- attr_reader :description, :parent, :overrides
233
-
234
- def initialize(parent:, subject_key:, description:, overrides:)
235
- @parent = parent
236
- @subject_key = subject_key
237
- @description = description
238
-
239
- @children = []
240
-
241
- factories = overrides.map do |key, override|
242
- parent_factory = parent.lookup_factory(key: key)
243
-
244
- factory = Build::Factory.new(
245
- parent: parent_factory,
246
- context: self,
247
- key: key,
248
- name: parent_factory&.name || BasicContext.default_factory_name(test_class: test_class, key: key),
249
- source_location: Kernel.caller_locations(0, 1).first
250
- )
251
-
252
- factory.add_values(override.values)
253
-
254
- factory
255
- end
256
-
257
- @factories = factories.map { |f| [f.key, f] }.to_h
258
- @named_factories = factories.map { |f| [f.name, f] }.to_h
259
- end
260
-
261
- def setup; end
262
-
263
- def subtree_root?
264
- false
265
- end
266
-
267
- def add_context(context, &block)
268
- @children << context
269
-
270
- context.setup
271
- end
272
-
273
- def test_class
274
- parent&.test_class || raise(StandardError, "No parent context defines a test class")
275
- end
276
-
277
- def subject_key
278
- @subject_key || (parent.subject_key unless subtree_root? || parent.nil?)
279
- end
280
-
281
- def context_path(subtree: true)
282
- add_context_to_path([], subtree: subtree).freeze
283
- end
284
-
285
- def full_description
286
- context_path.reduce(StringIO.new) do |description, context|
287
- description << " " if description.length.positive? && context.separate_description_by_space?
288
- description << context.description
289
- end.string
290
- end
291
-
292
- def root
293
- result = self
294
- result = result.parent until result.parent.nil?
295
- result
296
- end
297
-
298
- def add_factory(factory)
299
- raise KeyError, "Factory for type #{factory.key.type} already defined" if @factories.key?(factory.key.type)
300
- raise KeyError, "Factory with name #{factory.name} already defined" if @named_factories.key?(factory.name)
301
-
302
- @factories[factory.key] = factory
303
- @named_factories[factory.name] = factory
304
-
305
- test_class.__define_factory_method(factory: factory)
306
- end
307
-
308
- def factories
309
- @factories.dup.freeze
310
- end
311
-
312
- def named_factories
313
- @named_factories.dup.freeze
314
- end
315
-
316
- def lookup_factory(key:)
317
- @factories[key] || @parent&.lookup_factory(key: key)
318
- end
319
-
320
- def lookup_named_factory(name:)
321
- @named_factories[name] || @parent&.lookup_named_factory(name: name)
322
- end
323
-
324
- def create_subject(environment:, subject_key:)
325
- @parent.create_subject(environment: environment, subject_key: subject_key)
326
- end
327
-
328
- def create_result(subject:, arguments:, options:, block:)
329
- @parent.create_result(subject: subject, arguments: arguments, options: options, block: block)
330
- end
331
-
332
- protected
333
-
334
- def effective_overrides
335
- context_path.each_with_object({}) do |context, overrides|
336
- overrides.merge!(context.overrides) do |_key, existing_override, additional_override|
337
- existing_override.merge(additional_override)
338
- end
339
- end
340
- end
341
-
342
- def add_context_to_path(path, subtree: true)
343
- @parent.add_context_to_path(path, subtree: subtree) unless @parent.nil? || (subtree && subtree_root?)
344
-
345
- path << self
346
- end
347
-
348
- def separate_description_by_space?
349
- true
350
- end
351
- end
352
- end
353
- end