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.
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,17 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- using Calificador::Util::CoreExtensions
4
-
5
3
  module Calificador
6
- module Spec
4
+ module Context
7
5
  # Factory class
8
6
  class TestMethod < BasicContext
9
- class Dsl < BasicContext::Dsl
10
- def body(&block)
11
- delegate.body = block
12
- end
13
- end
14
-
15
7
  attr_reader :body, :expected_to_fail
16
8
 
17
9
  def initialize(parent:, subject_key:, description:, overrides:, expected_to_fail: false, body:)
@@ -28,7 +20,7 @@ module Calificador
28
20
 
29
21
  test_class.class_eval(<<~METHOD, file, line_number)
30
22
  define_method(test_method_name) do
31
- __run_test(test_method: test_method)
23
+ __run_test(context: test_method)
32
24
  end
33
25
  METHOD
34
26
 
@@ -38,27 +30,6 @@ module Calificador
38
30
  def method_name
39
31
  @method_name ||= "test_: #{full_description}"
40
32
  end
41
-
42
- def run_test(test:)
43
- body = self.body
44
- environment = TestEnvironment.new(test: test, context: self)
45
-
46
- if expected_to_fail
47
- passed = begin
48
- environment.instance_exec(&body)
49
- true
50
- rescue ::Minitest::Assertion => e
51
- test.pass(e.message)
52
- false
53
- end
54
-
55
- test.flunk("Expected test to fail") if passed
56
- else
57
- environment.instance_exec(&body)
58
- end
59
-
60
- environment.__done
61
- end
62
33
  end
63
34
  end
64
35
  end
@@ -1,20 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- using Calificador::Util::CoreExtensions
4
-
5
3
  module Calificador
6
- module Spec
7
- class TestRoot < ExamineContext
8
- class Dsl < ExamineContext::Dsl
9
- def body(&block)
10
- delegate.body = block
11
- end
12
- end
13
-
4
+ module Context
5
+ class TestRoot < InstanceContext
14
6
  attr_accessor :test_class
15
7
 
16
8
  def initialize(test_class:, subject_key:, description:, &body)
17
- super(parent: nil, subject_key: subject_key, description: description, overrides: {})
9
+ super(parent: nil, subject_key: subject_key, description: description)
18
10
 
19
11
  @test_class = test_class
20
12
  @body = body
@@ -30,10 +22,6 @@ module Calificador
30
22
  super
31
23
  end
32
24
 
33
- def create_result(subject:, arguments:, options:, block:)
34
- subject
35
- end
36
-
37
25
  protected
38
26
 
39
27
  def setup_body
@@ -1,24 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- using Calificador::Util::CoreExtensions
4
-
5
3
  module Calificador
6
- module Spec
7
- class ExamineContext < BasicContext
8
- class Dsl < BasicContext::Dsl
9
- end
4
+ module Context
5
+ class TypeContext < BasicContext
6
+ def initialize(parent:, subject_key:, description:, overrides: [])
7
+ raise ArgumentError, "Subject must be a must be a #{BasicObject}" unless subject_key.type <= BasicObject
10
8
 
11
- def initialize(parent:, subject_key:, description:, overrides: {})
12
9
  super(
13
10
  parent: parent,
14
11
  description: description,
15
12
  subject_key: subject_key,
16
- overrides: overrides,
13
+ overrides: overrides
17
14
  )
18
15
  end
19
16
 
20
- def create_subject(environment:, subject_key:)
21
- environment.create_object(key: subject_key)
17
+ def create_subject(environment:)
18
+ subject_key.type
22
19
  end
23
20
 
24
21
  def subtree_root?
@@ -2,27 +2,36 @@
2
2
 
3
3
  require "minitest"
4
4
 
5
- using Calificador::Util::CoreExtensions
6
-
7
5
  module Calificador
8
6
  # Test subject key
9
7
  class Key
8
+ NO_TRAIT = :"<none>"
10
9
  DEFAULT_TRAIT = :"<default>"
11
- INHERITED_TRAIT = :"<inherited>"
12
10
 
13
11
  class << self
14
- def [](type, trait = DEFAULT_TRAIT)
12
+ def [](type, trait = NO_TRAIT)
15
13
  new(type: type, trait: trait)
16
14
  end
17
15
  end
18
16
 
19
17
  attr_reader :type, :trait
20
18
 
21
- def initialize(type:, trait: DEFAULT_TRAIT)
22
- raise ArgumentError, "Illegal trait value #{trait}" if trait == INHERITED_TRAIT
19
+ def initialize(type:, trait: NO_TRAIT)
20
+ trait ||= NO_TRAIT
21
+
22
+ raise ArgumentError, "Type must be a #{Module}, not '#{type}' (#{type.class})" unless type.is_a?(Module)
23
+ raise ArgumentError, "Trait must be a #{Symbol}" unless trait.is_a?(Symbol)
23
24
 
24
25
  @type = type
25
- @trait = trait || DEFAULT_TRAIT
26
+ @trait = trait
27
+ end
28
+
29
+ def trait?
30
+ @trait != NO_TRAIT && @trait != DEFAULT_TRAIT
31
+ end
32
+
33
+ def default_trait?
34
+ @trait == DEFAULT_TRAIT
26
35
  end
27
36
 
28
37
  def hash
@@ -30,30 +39,33 @@ module Calificador
30
39
  end
31
40
 
32
41
  def ==(other)
33
- (@type == other.type) && (@trait == other.trait)
42
+ other.is_a?(Key) && (@type == other.type) && (@trait == other.trait)
34
43
  end
35
44
 
36
45
  alias_method :eql?, :==
37
46
 
38
- def to_s
39
- trait == DEFAULT_TRAIT ? type.to_s : "#{type} (#{trait})"
47
+ def to_s(base_module: nil)
48
+ type_name = @type.name_without_common_parents(base: base_module)
49
+ @trait == NO_TRAIT ? type_name : "#{type_name} (#{@trait})"
40
50
  end
41
51
 
42
52
  alias_method :inspect, :to_s
43
53
 
44
54
  def with(trait)
45
55
  case trait
46
- when INHERITED_TRAIT
47
- self
48
56
  when nil, DEFAULT_TRAIT
49
- @trait == DEFAULT_TRAIT ? self : Key.new(type: @type, trait: DEFAULT_TRAIT)
57
+ self
50
58
  else
51
59
  trait == @trait ? self : Key.new(type: @type, trait: trait)
52
60
  end
53
61
  end
54
62
 
55
- def trait?
56
- @trait != DEFAULT_TRAIT
63
+ def with_default(key)
64
+ if @trait == DEFAULT_TRAIT && @trait != key.trait
65
+ Key[@type, key.trait]
66
+ else
67
+ self
68
+ end
57
69
  end
58
70
  end
59
71
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "minitest/test"
4
4
 
5
- using Calificador::Util::CoreExtensions
6
-
7
5
  module Calificador
8
6
  module Minitest
9
7
  # Patches to minitest classes
@@ -2,11 +2,9 @@
2
2
 
3
3
  require "minitest"
4
4
 
5
- using Calificador::Util::CoreExtensions
6
-
7
5
  module Calificador
8
6
  # Base class for unit tests
9
7
  class Test < ::Minitest::Test
10
- include TestMixin
8
+ prepend TestMixin
11
9
  end
12
10
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "minitest"
4
-
5
- using Calificador::Util::CoreExtensions
4
+ require "forwardable"
6
5
 
7
6
  module Calificador
8
7
  # Mixin for unit tests
9
8
  module TestMixin
9
+ extend Forwardable
10
+
10
11
  Key = Calificador::Key
11
12
 
12
13
  class << self
@@ -19,189 +20,155 @@ module Calificador
19
20
  end
20
21
  end
21
22
 
22
- def __run_test(test_method:)
23
+ def singleton_method_added(method)
24
+ raise "Adding singleton methods (#{method}) to tests is not supported here" if __current_test_environment?
25
+
26
+ super
27
+ end
28
+
29
+ def_delegator :__current_test_environment, :subject
30
+ def_delegator :__current_test_environment, :create
31
+
32
+ def __run_test(context:)
23
33
  stack_marker = "calificador_test_#{SecureRandom.uuid.gsub("-", "_")}"
24
34
 
25
- __register_current_run(stack_marker: stack_marker, test_method: test_method)
35
+ environment = Context::TestEnvironment.new(parent: context, test_instance: self)
36
+ __register_current_environment(stack_marker: stack_marker, environment: environment)
26
37
 
27
38
  begin
28
39
  instance_eval(<<~METHOD, stack_marker, 1)
29
- test_method.run_test(test: self)
40
+ environment.run_test
30
41
  METHOD
31
42
  ensure
32
- __unregister_current_run(stack_marker: stack_marker)
43
+ __unregister_current_environment(stack_marker: stack_marker)
33
44
  end
34
45
  end
35
46
 
36
47
  protected
37
48
 
38
- TestRun = Struct.new(:test_instance, :test_method, keyword_init: true)
39
-
40
- def __register_current_run(stack_marker:, test_method:)
41
- @__calificador_current_test_run = TestRun.new(test_instance: self, test_method: test_method)
42
- Calificador::Test.__register_current_run(stack_marker: stack_marker, test_run: @__calificador_current_test_run)
49
+ def __register_current_environment(stack_marker:, environment:)
50
+ @__calificador_current_test_environment = environment
51
+ Calificador::Test.__register_current_environment(stack_marker: stack_marker, environment: environment)
43
52
  end
44
53
 
45
- def __unregister_current_run(stack_marker:)
46
- if !instance_variable_defined?(:@__calificador_current_test_run) || @__calificador_current_test_run.nil?
54
+ def __unregister_current_environment(stack_marker:)
55
+ if !instance_variable_defined?(:@__calificador_current_test_environment) || @__calificador_current_test_environment.nil?
47
56
  raise StandardError, "No current test run registered"
48
57
  end
49
58
 
50
- Calificador::Test.__unregister_current_run(stack_marker: stack_marker)
51
- @__calificador_current_test_run = nil
59
+ Calificador::Test.__unregister_current_environment(stack_marker: stack_marker)
60
+ @__calificador_current_test_environment = nil
52
61
  end
53
62
 
54
- def __current_test_run
55
- if !instance_variable_defined?(:@__calificador_current_test_run) || @__calificador_current_test_run.nil?
63
+ def __current_test_environment
64
+ if !instance_variable_defined?(:@__calificador_current_test_environment) || @__calificador_current_test_environment.nil?
56
65
  raise StandardError, "No current test run registered"
57
66
  end
58
67
 
59
- @__calificador_current_test_run
68
+ @__calificador_current_test_environment
69
+ end
70
+
71
+ def __current_test_environment?
72
+ instance_variable_defined?(:@__calificador_current_test_environment) || !@__calificador_current_test_environment
60
73
  end
61
74
 
62
75
  # Class methods for unit tests
63
76
  module ClassMethods
77
+ extend Forwardable
78
+
64
79
  def run_all_tests(reporter: MiniTest::CompositeReporter.new)
65
80
  runnable_methods.each do |method|
66
81
  run_one_method(self, method, reporter)
67
82
  end
68
83
  end
69
84
 
70
- def examines(type = MISSING, trait: MISSING)
71
- self.__subject_type = type unless type.equal?(MISSING)
72
- self.__subject_trait = trait unless trait.equal?(MISSING)
73
-
74
- Key[__subject_type, __subject_trait]
75
- end
76
-
77
- def factory(type, description = nil, name: nil, &block)
78
- __root_context.dsl_config do
79
- factory(type, description, name: nil, &block)
80
- end
81
- end
82
-
83
- def examine(subject_type, description = nil, trait: Key::INHERITED_TRAIT, **values, &block)
84
- __root_context.dsl_config do
85
- examine(subject_type, description, trait: trait, **values, &block)
86
- end
87
- end
88
-
89
- def method(method, description = nil, **values, &block)
90
- __root_context.dsl_config do
91
- method(method, description, &block)
85
+ def examines(subject_class, *description_or_trait_or_init)
86
+ if instance_variable_defined?(:@__calificador_root_context) && @__calificador_root_context
87
+ raise <<~MESSAGE.gsub("\n", " ")
88
+ Cannot specify 'examines' after a test context has been defined. The 'examines' statement should be the first statement in your test class
89
+ MESSAGE
92
90
  end
93
- end
94
91
 
95
- def class_method(method, description = nil, **values, &block)
96
- __root_context.dsl_config do
97
- class_method(method, description, &block)
98
- end
99
- end
92
+ arguments = Context::BasicContext.extract_arguments(
93
+ subject_key: Key[subject_class],
94
+ values: description_or_trait_or_init,
95
+ names: %i[description trait init]
96
+ )
100
97
 
101
- def must(description, trait: Key::INHERITED_TRAIT, **values, &block)
102
- __root_context.dsl_config do
103
- must(description, trait: trait, **values, &block)
104
- end
105
- end
98
+ arguments.description ||= __default_instance_description(subject_key: arguments.subject_key)
106
99
 
107
- def must_fail(description, trait: Key::INHERITED_TRAIT, **values, &block)
108
- __root_context.dsl_config do
109
- must_fail(description, trait: trait, **values, &block)
110
- end
100
+ @__calificador_root_context = Context::TestRoot.new(
101
+ test_class: self,
102
+ subject_key: arguments.subject_key,
103
+ description: arguments.description
104
+ )
111
105
  end
112
106
 
113
- def with(description, trait: Key::INHERITED_TRAIT, **values, &block)
114
- __root_context.dsl_config do
115
- with(description, trait: trait, **values, &block)
116
- end
117
- end
118
-
119
- def without(description, trait: Key::INHERITED_TRAIT, **values, &block)
120
- __root_context.dsl_config do
121
- without(description, trait: trait, **values, &block)
122
- end
123
- end
124
-
125
- def where(description, trait: Key::INHERITED_TRAIT, **values, &block)
126
- __root_context.dsl_config do
127
- where(description, trait: trait, **values, &block)
128
- end
129
- end
107
+ def_delegators :__calificador_current_context, :factory, :mock
108
+ def_delegators :__calificador_current_context, :type, :examine, :operation, :with, :without, :where
109
+ def_delegators :__calificador_current_context, :must, :must_fail
110
+ def_delegators :__calificador_current_context, :args, :props
130
111
 
131
112
  def body(&block)
132
113
  class_eval(&block)
133
114
  end
134
115
 
135
- def __subject_type
136
- if !instance_variable_defined?(:@__calificador_subject_type) || @__calificador_subject_type.nil?
137
- type_name = name.gsub(%r{(?<=\w)Test\z}, "")
138
-
139
- if Kernel.const_defined?(type_name)
140
- @__calificador_subject_type = Kernel.const_get(type_name)
141
- else
142
- raise StandardError, "Cannot determine test subject type from test class name '#{name}'"
143
- end
144
- end
145
-
146
- @__calificador_subject_type
116
+ def __default_type_description(subject_key:)
117
+ subject_key.type.name_without_common_parents(base: self)
147
118
  end
148
119
 
149
- def __subject_type=(type)
150
- if instance_variable_defined?(:@__calificador_subject_type) && !@__calificador_subject_type.nil?
151
- raise StandardError, "Cannot redefine test subject type"
152
- end
120
+ def __default_instance_description(subject_key:)
121
+ description = StringIO.new
153
122
 
154
- @__calificador_subject_type = type
155
- end
123
+ description << subject_key.type.name_without_common_parents(base: self)
124
+ description << "(" << subject_key.trait.to_s.gsub("_", " ") << ")" if subject_key.trait?
156
125
 
157
- def __subject_trait
158
- if !instance_variable_defined?(:@__calificador_subject_trait) || @__calificador_subject_trait.nil?
159
- @__calificador_subject_trait = Key::DEFAULT_TRAIT
160
- end
161
-
162
- @__calificador_subject_trait
126
+ description.string
163
127
  end
164
128
 
165
- def __subject_trait=(trait)
166
- if instance_variable_defined?(:@__calificador_subject_trait) && !@__calificador_subject_trait.nil?
167
- raise StandardError, "Cannot redefine test subject trait"
168
- end
129
+ def __default_factory_name(subject_key:)
130
+ description = StringIO.new
131
+
132
+ description << subject_key.type.base_name.snake_case
133
+ description << "_" << subject_key.trait.to_s if subject_key.trait?
169
134
 
170
- @__calificador_subject_trait = trait
135
+ description.string
171
136
  end
172
137
 
173
138
  def __root_context
174
139
  if !instance_variable_defined?(:@__calificador_root_context) || @__calificador_root_context.nil?
175
- description = __subject_type.name.delete_prefix(parent_prefix)
176
- description = "#{description} {#{__subject_trait}}" unless __subject_trait.equal?(Key::DEFAULT_TRAIT)
140
+ subject_key = __calificador_guess_subject_key
177
141
 
178
- @__calificador_root_context = Spec::TestRoot.new(
142
+ @__calificador_root_context = Context::TestRoot.new(
179
143
  test_class: self,
180
- subject_key: Key[__subject_type, __subject_trait],
181
- description: description
144
+ subject_key: subject_key,
145
+ description: __default_instance_description(subject_key: subject_key)
182
146
  )
183
147
  end
184
148
 
185
149
  @__calificador_root_context
186
150
  end
187
151
 
188
- def __register_current_run(stack_marker:, test_run:)
152
+ def __register_current_environment(stack_marker:, environment:)
189
153
  __calificador_test_lock.synchronize do
190
- raise KeyError, "Test run #{stack_marker} already registered" if __calificador_test_runs.key?(stack_marker)
154
+ if __calificador_test_environments.key?(stack_marker)
155
+ raise KeyError,
156
+ "Test run #{stack_marker} already registered"
157
+ end
191
158
 
192
- __calificador_test_runs[stack_marker] = test_run
159
+ __calificador_test_environments[stack_marker] = environment
193
160
  end
194
161
  end
195
162
 
196
- def __unregister_current_run(stack_marker:)
163
+ def __unregister_current_environment(stack_marker:)
197
164
  __calificador_test_lock.synchronize do
198
- if __calificador_test_runs.delete(stack_marker).nil?
165
+ if __calificador_test_environments.delete(stack_marker).nil?
199
166
  raise KeyError, "Could not unregister test #{stack_marker}"
200
167
  end
201
168
  end
202
169
  end
203
170
 
204
- def __current_test_run
171
+ def __current_test_environment
205
172
  __calificador_test_lock.synchronize do
206
173
  location = ::Kernel.caller_locations.find do |l|
207
174
  %r{\Acalificador_test_[a-zA-Z0-9_]+\z} =~ l.path
@@ -209,49 +176,59 @@ module Calificador
209
176
 
210
177
  raise StandardError, "Could not find current test run in call stack" unless location
211
178
 
212
- __calificador_test_runs.fetch(location.path) do
179
+ __calificador_test_environments.fetch(location.path) do
213
180
  raise KeyError, "No test run registered for #{location.path}"
214
181
  end
215
182
  end
216
183
  end
217
184
 
218
- def __factory_methods
219
- @__calificador_factory_methods = Set.new unless instance_variable_defined?(:@__calificador_factory_methods)
185
+ def __calificador_configure(context:, block:)
186
+ __calificador_push_context(context)
220
187
 
221
- @__calificador_factory_methods.dup.freeze
188
+ begin
189
+ class_exec(&block)
190
+ ensure
191
+ __calificador_pop_context
192
+ end
222
193
  end
223
194
 
224
- def __define_factory_method(factory:)
225
- @__calificador_factory_methods = Set.new unless instance_variable_defined?(:@__calificador_factory_methods)
226
-
227
- if @__calificador_factory_methods.add?(factory.name)
228
- factory_method_name = if factory.name.to_s.start_with?("test_")
229
- "create_#{factory.name}"
230
- else
231
- factory.name
232
- end
195
+ def method_added(method)
196
+ if !method.start_with?("test_: ") && __calificador_contexts.size > 1
197
+ raise "Adding methods (#{method}) to tests is not supported here"
198
+ end
233
199
 
234
- if method_defined?(factory_method_name, true)
235
- raise "Cannot define factory method #{factory_method_name}, method already exists in #{self.class}"
236
- end
200
+ super
201
+ end
237
202
 
238
- type = factory.key.type # rubocop:disable Lint/UselessAssignment
239
- trait = factory.key.trait # rubocop:disable Lint/UselessAssignment
203
+ protected
240
204
 
241
- class_eval(<<~METHOD, factory.source_location.first, factory.source_location.last)
242
- define_method(factory_method_name) do
243
- __current_test_run.test_method.create(type: type, trait: trait)
244
- end
245
- METHOD
205
+ def __calificador_contexts
206
+ if !instance_variable_defined?(:@__calificador_contexts) || @__calificador_contexts.nil?
207
+ @__calificador_contexts = [__root_context]
246
208
  end
209
+
210
+ @__calificador_contexts
247
211
  end
248
212
 
249
- protected
213
+ def __calificador_push_context(context)
214
+ __calificador_contexts.push(context)
215
+ context
216
+ end
250
217
 
251
- def __calificador_test_runs
252
- @__calificador_test_runs = {} unless instance_variable_defined?(:@__calificador_test_runs)
218
+ def __calificador_pop_context
219
+ raise "Cannot remove root context" unless __calificador_contexts.size > 1
220
+
221
+ __calificador_contexts.pop
222
+ end
253
223
 
254
- @__calificador_test_runs
224
+ def __calificador_current_context
225
+ __calificador_contexts.last
226
+ end
227
+
228
+ def __calificador_test_environments
229
+ @__calificador_test_environments = {} unless instance_variable_defined?(:@__calificador_test_environments)
230
+
231
+ @__calificador_test_environments
255
232
  end
256
233
 
257
234
  def __calificador_test_lock
@@ -259,6 +236,33 @@ module Calificador
259
236
 
260
237
  @__calificador_test_lock
261
238
  end
239
+
240
+ def __calificador_guess_subject_key
241
+ type_name = name&.gsub(%r{(?<=\w)Test\z}, "")
242
+
243
+ unless type_name
244
+ raise StandardError, <<~MESSAGE.gsub("\n", " ")
245
+ Cannot guess test subject class from test class name '#{name}'. Please use 'examines' to specify the test subject.
246
+ MESSAGE
247
+ end
248
+
249
+ unless Kernel.const_defined?(type_name)
250
+ raise StandardError, <<~MESSAGE.gsub("\n", " ")
251
+ Guessed test subject type (#{type_name}) does not exist. Please use 'examines' to specify the test subject.
252
+ MESSAGE
253
+ end
254
+
255
+ subject_type = Kernel.const_get(type_name)
256
+
257
+ if subject_type <= Singleton || subject_type <= BasicObject || subject_type.is_a?(Module)
258
+ Key[subject_type]
259
+ else
260
+ raise StandardError, <<~MESSAGE.gsub("\n", " ")
261
+ Guessed test subject type (#{subject_type}) is not a Class, a Module or a Singleton. Please use 'examines'
262
+ to specify the test subject.
263
+ MESSAGE
264
+ end
265
+ end
262
266
  end
263
267
  end
264
268
  end