smartest 0.3.3.alpha2 → 0.3.3.alpha4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 396af151370001b4762c6c1a71426fef7c2e0773626080af62496bc068afdc02
4
- data.tar.gz: 0b5b6e9e76582dfd91df63fe7e2928fbbd6d0a90101e8f3d9834a91d7b94c109
3
+ metadata.gz: 75e3f0538afd3f490c37121ae4eee497a4691f35926ff50201e035acb64d2314
4
+ data.tar.gz: 53fea3bdbbb788167d13c6a7b6c18a9258de42b971c4f2a1f36906176f75813a
5
5
  SHA512:
6
- metadata.gz: 320397233b8b2e0c46b5d44d6a8bc892d44bfccfc9b63a1db31f868fdc3ac7da7c500902f93b36124f61119dc23b05a3238d3032d7e814defed8c7f5b57f9ef1
7
- data.tar.gz: 0b507415bf8e31472e4117371474226fbec62802be8a41d002cc38403967d2a877e60d2a7fb0d0695490e416e1fcf208928f3a6882620208c4d694f425138b37
6
+ metadata.gz: ba646897bc5994e7caf30fc9b1127a1ad72f2545a4ed8734820c5eeeca3e5e1e7e0302394b38916fbc9d9c153dccfdae96496093dce1f23af172fa74a01cbd75
7
+ data.tar.gz: 5c12f0c3532da8eb44c3e832d28e479b1f8a54f5797a4c67c61c865bbbedf31e2225fe441c1f433d8de6ce3b2246c374de998d2aae9b2cfa8a7ea63f043ed949
data/DEVELOPMENT.md CHANGED
@@ -46,6 +46,7 @@ smartest/
46
46
  expectation_target.rb
47
47
  matchers.rb
48
48
  simple_stub.rb
49
+ constant_stub_helpers.rb
49
50
 
50
51
  runner.rb
51
52
  test_result.rb
data/README.md CHANGED
@@ -613,6 +613,18 @@ class PaymentFixture < Smartest::Fixture
613
613
  end
614
614
  ```
615
615
 
616
+ Register the fixture class from `around_suite` before tests request the fixture:
617
+
618
+ ```ruby
619
+ around_suite do |suite|
620
+ use_fixture PaymentFixture
621
+ suite.run
622
+ end
623
+ ```
624
+
625
+ `use_fixture` is available inside `around_suite` or `around_test` blocks, not as
626
+ a top-level method in a test file.
627
+
616
628
  The stub affects existing instances and new instances of the target class in
617
629
  the current Fiber until it is reset. Other Fibers and Threads continue to see
618
630
  the original method unless they apply their own stub. Tests can request the
@@ -625,10 +637,16 @@ end
625
637
  ```
626
638
 
627
639
  Use `simple_stub(Time, :now) { fixed_time }` for singleton methods such as class
628
- methods. Both helpers call `Smartest::SimpleStub` internally, apply the stub,
629
- register `cleanup { stub.reset }`, and return the stub object. They are
630
- available inside `Smartest::Fixture` fixture blocks because they need cleanup to
631
- tie the stub lifetime to the fixture scope.
640
+ methods.
641
+
642
+ Use `simple_stub_const("AppConfig::PAYMENT_PROVIDER", "fake") { ... }` for
643
+ constants in test bodies, `around_test`, or `around_suite`. Constant stubs are
644
+ process-global; avoid concurrent tests that stub the same constant.
645
+
646
+ The method stub helpers call `Smartest::SimpleStub` internally, apply the stub,
647
+ register `cleanup { stub.reset }`, and return the stub object.
648
+ `simple_stub_const` records the previous constant value, replaces it, yields to
649
+ the block, and restores or removes the constant with `ensure`.
632
650
 
633
651
  `Smartest::SimpleStub#apply` and `#reset` are idempotent in the current Fiber.
634
652
  `apply!` raises
@@ -862,7 +880,7 @@ Smartest currently focuses on a small runner API:
862
880
  - fixture dependencies through keyword arguments
863
881
  - fixture cleanup
864
882
  - suite-scoped fixtures through `suite_fixture`
865
- - fixture-scoped method stubs through `simple_stub_any_instance_of` and `simple_stub`
883
+ - fixture-scoped method stubs and block-scoped constant stubs
866
884
  - suite hooks with `around_suite`
867
885
  - test hooks with `around_test`
868
886
  - skipped and pending tests through `skip` and `pending`
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smartest
4
+ module ConstantStubHelpers
5
+ private
6
+
7
+ def simple_stub_const(constant_path, value)
8
+ raise ArgumentError, "simple_stub_const block is required" unless block_given?
9
+
10
+ owner, constant_name = resolve_simple_stub_constant(constant_path)
11
+ original_defined = owner.const_defined?(constant_name, false)
12
+ original_value = owner.const_get(constant_name, false) if original_defined
13
+
14
+ owner.__send__(:remove_const, constant_name) if original_defined
15
+ owner.const_set(constant_name, value)
16
+
17
+ yield
18
+ ensure
19
+ if owner && constant_name
20
+ owner.__send__(:remove_const, constant_name) if owner.const_defined?(constant_name, false)
21
+ owner.const_set(constant_name, original_value) if original_defined
22
+ end
23
+ end
24
+
25
+ def resolve_simple_stub_constant(constant_path)
26
+ unless constant_path.is_a?(String) || constant_path.is_a?(Symbol)
27
+ raise ArgumentError, "constant path must be a String or Symbol"
28
+ end
29
+
30
+ names = constant_path.to_s.split("::")
31
+ names.shift if names.first == ""
32
+ raise ArgumentError, "constant path must not be empty" if names.empty?
33
+
34
+ names.each do |name|
35
+ raise ArgumentError, "invalid constant path: #{constant_path}" unless name.match?(/\A[A-Z]\w*\z/)
36
+ end
37
+
38
+ constant_name = names.pop.to_sym
39
+ owner = names.reduce(Object) { |namespace, name| namespace.const_get(name, false) }
40
+ raise ArgumentError, "constant owner must be a Module or Class" unless owner.is_a?(Module)
41
+
42
+ [owner, constant_name]
43
+ end
44
+ end
45
+ end
@@ -4,6 +4,7 @@ module Smartest
4
4
  class ExecutionContext
5
5
  include Expectations
6
6
  include Matchers
7
+ include ConstantStubHelpers
7
8
 
8
9
  def initialize(run_state: TestRunState.new)
9
10
  @run_state = run_state
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Smartest
4
4
  class Fixture
5
- RESERVED_CONTEXT_METHODS = %i[skip pending].freeze
5
+ RESERVED_CONTEXT_METHODS = %i[skip pending simple_stub_const].freeze
6
6
 
7
7
  class << self
8
8
  def fixture(name, scope: :test, &block)
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Smartest
4
4
  class AroundSuiteContext
5
+ include ConstantStubHelpers
6
+
5
7
  def initialize(suite)
6
8
  @suite = suite
7
9
  end
@@ -30,6 +32,8 @@ module Smartest
30
32
  end
31
33
 
32
34
  class AroundTestContext
35
+ include ConstantStubHelpers
36
+
33
37
  def initialize(test_run, run_state:)
34
38
  @test_run = test_run
35
39
  @run_state = run_state
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Smartest
4
- VERSION = "0.3.3.alpha2"
4
+ VERSION = "0.3.3.alpha4"
5
5
  end
data/lib/smartest.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative "smartest/version"
4
4
  require_relative "smartest/errors"
5
5
  require_relative "smartest/simple_stub"
6
+ require_relative "smartest/constant_stub_helpers"
6
7
  require_relative "smartest/parameter_extractor"
7
8
  require_relative "smartest/test_case"
8
9
  require_relative "smartest/test_registry"
@@ -63,6 +63,10 @@ class SimpleStubSelfTestClock
63
63
  end
64
64
  end
65
65
 
66
+ module SimpleStubSelfTestConfig
67
+ PROVIDER = :original_provider
68
+ end
69
+
66
70
  test("simple stub stubs instance methods until reset") do
67
71
  existing = SimpleStubSelfTestSubject.new("Alice")
68
72
  stub = Smartest::SimpleStub.new(SimpleStubSelfTestSubject, :name) { "stubbed" }
@@ -154,6 +158,103 @@ test("simple_stub applies and resets singleton methods from fixture cleanup") do
154
158
  expect(status).to eq(0)
155
159
  end
156
160
 
161
+ test("simple_stub_const applies and resets existing constants in test body blocks") do
162
+ result = simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :stubbed_provider) do
163
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:stubbed_provider)
164
+ :block_result
165
+ end
166
+
167
+ expect(result).to eq(:block_result)
168
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:original_provider)
169
+ end
170
+
171
+ test("simple_stub_const removes newly defined constants after test body blocks") do
172
+ simple_stub_const("SimpleStubSelfTestConfig::MISSING_PROVIDER", :stubbed_missing_provider) do
173
+ expect(SimpleStubSelfTestConfig::MISSING_PROVIDER).to eq(:stubbed_missing_provider)
174
+ end
175
+
176
+ expect(SimpleStubSelfTestConfig.const_defined?(:MISSING_PROVIDER, false)).to eq(false)
177
+ end
178
+
179
+ test("simple_stub_const restores constants when the block raises") do
180
+ error = SimpleStubSelfTest.capture_error(RuntimeError) do
181
+ simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :stubbed_provider) do
182
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:stubbed_provider)
183
+ raise "stubbed block failed"
184
+ end
185
+ end
186
+
187
+ expect(error.message).to eq("stubbed block failed")
188
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:original_provider)
189
+ end
190
+
191
+ test("simple_stub_const requires a block") do
192
+ error = SimpleStubSelfTest.capture_error(ArgumentError) do
193
+ simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :stubbed_provider)
194
+ end
195
+
196
+ expect(error.message).to eq("simple_stub_const block is required")
197
+ end
198
+
199
+ test("simple_stub_const wraps around_test hooks") do
200
+ suite = Smartest::Suite.new
201
+ suite.around_test_hooks << proc do |test_run|
202
+ simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :around_test_provider) do
203
+ test_run.run
204
+ end
205
+ end
206
+ suite.tests.add(
207
+ SimpleStubSelfTest.test_case(
208
+ "uses around_test constant stub",
209
+ proc { expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:around_test_provider) }
210
+ )
211
+ )
212
+
213
+ status, = SimpleStubSelfTest.run_suite(suite)
214
+
215
+ expect(status).to eq(0)
216
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:original_provider)
217
+ end
218
+
219
+ test("simple_stub_const wraps around_suite hooks") do
220
+ suite = Smartest::Suite.new
221
+ suite.around_suite_hooks << proc do |suite_run|
222
+ simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :around_suite_provider) do
223
+ suite_run.run
224
+ end
225
+ end
226
+ suite.tests.add(
227
+ SimpleStubSelfTest.test_case(
228
+ "uses around_suite constant stub",
229
+ proc { expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:around_suite_provider) }
230
+ )
231
+ )
232
+
233
+ status, = SimpleStubSelfTest.run_suite(suite)
234
+
235
+ expect(status).to eq(0)
236
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:original_provider)
237
+ end
238
+
239
+ test("simple_stub_const is not available inside fixture blocks") do
240
+ fixture_class = Class.new(Smartest::Fixture) do
241
+ fixture :bad_constant_stub do
242
+ simple_stub_const("SimpleStubSelfTestConfig::PROVIDER", :fixture_provider) { :fixture_provider }
243
+ end
244
+ end
245
+
246
+ suite = Smartest::Suite.new
247
+ suite.fixture_classes.add(fixture_class)
248
+ suite.tests.add(SimpleStubSelfTest.test_case("uses bad fixture", proc { |bad_constant_stub:| bad_constant_stub }))
249
+
250
+ status, output = SimpleStubSelfTest.run_suite(suite)
251
+
252
+ expect(status).to eq(1)
253
+ expect(output).to include("NoMethodError")
254
+ expect(output).to include("simple_stub_const")
255
+ expect(SimpleStubSelfTestConfig::PROVIDER).to eq(:original_provider)
256
+ end
257
+
157
258
  test("simple stub preserves receiver self and method blocks") do
158
259
  subject = SimpleStubSelfTestSubject.new("Alice")
159
260
  stub = Smartest::SimpleStub.new(SimpleStubSelfTestSubject, :yielding_greeting) do |prefix, &block|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3.alpha2
4
+ version: 0.3.3.alpha4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Iwaki
@@ -44,6 +44,7 @@ files:
44
44
  - lib/smartest.rb
45
45
  - lib/smartest/autorun.rb
46
46
  - lib/smartest/cli_arguments.rb
47
+ - lib/smartest/constant_stub_helpers.rb
47
48
  - lib/smartest/dsl.rb
48
49
  - lib/smartest/errors.rb
49
50
  - lib/smartest/execution_context.rb