Add Micro::Case::Success/Failure test-double factories#155
Open
serradura wants to merge 3 commits into
Open
Conversation
…#6) `Micro::Case::Result::Success.new(data:, type:, use_case:)` and `Micro::Case::Result::Failure.new(...)` fabricate result instances in tests without running a real use case. `Success.to_yield(...)` and `Failure.to_yield(...)` return a `Micro::Case::Result::Wrapper` for stubbing block-form `Micro::Case.call(input) { |on| ... }` consumers. Opt-in: the gem does NOT auto-require the factories. Add `require 'micro/case/with_test_doubles'` to spec_helper.rb / test_helper.rb to enable them. Production load paths unaffected. Strictly additive — calls flow through `Result#__set__`, so the curated `Error::InvalidResultType` / `InvalidResult` / `InvalidUseCase` exceptions raise on bad input and no-op under `config.disable_runtime_checks = true`. Constants are modules, not classes — `result.class == Micro::Case::Result` (no subclass surface). Includes a runnable `examples/test_doubles/` project with paired RSpec and Minitest+Mocha suites demonstrating both return-value stubbing (`and_return` / `returns`) and block-form stubbing (`and_yield` / `yields`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new factories ship under the existing 5.7.1 line. CHANGELOG entry moves to an [Unreleased] section so it can attach to whatever version ships next, and READMEs / example Gemfile go back to 5.7.x references. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ase::{Success,Failure}
Per the updated issue spec, the constants live one segment higher in the
namespace tree:
- lib/micro/case/result/{success,failure}.rb
-> lib/micro/case/{success,failure}.rb
- Micro::Case::Result::{Success,Failure}
-> Micro::Case::{Success,Failure}
The shorter path reads cleaner at call sites and mirrors the sibling
`solid-result` gem layout (`Solid::Success` / `Solid::Failure`). The
constants remain modules (no subclass surface) and the returned object
is still a plain `Micro::Case::Result`.
Both READMEs gain a brief note about the bareword `Success` / `Failure`
shadowing concern: now that the constants live directly under
`Micro::Case`, a bare-token `Success` or `Failure` inside a `call!`
method body resolves to the constant rather than the helper method.
In practice every realistic call site has args, so Ruby parses them
as method calls regardless.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Micro::Case::Success.new(data:, type:, use_case:)andMicro::Case::Failure.new(...)for fabricating result instances in tests without running a real use case.Success.to_yield(...)/Failure.to_yield(...)return aMicro::Case::Result::Wrapperfor stubbing block-formMicro::Case.call(input) { |on| ... }consumers (e.g. RSpec'sand_yield, Mocha'syields).require 'micro/case/with_test_doubles'tospec/spec_helper.rbortest/test_helper.rbto enable them. Production load paths are unaffected.Result#__set__, so the curatedError::InvalidResultType/InvalidResult/InvalidUseCaseexceptions still raise on bad input and no-op underconfig.disable_runtime_checks = true. Constants are modules, not classes —result.class == Micro::Case::Result(no subclass surface).examples/test_doubles/project demonstrating both stubbing shapes with paired RSpec and Minitest+Mocha suites (FetchEmailcollaborator +SendInvitereturn-value consumer +DeliverReferralblock-form consumer).CHANGELOG.md(Keep a Changelog) under[Unreleased]and adds a new Testing with test doubles section toREADME.md+README.pt-BR.mdin lockstep. No version bump in this PR — that ships separately when the next release is cut.Closes #6.
What's new
lib/micro/case/success.rbMicro::Case::Success.new/.to_yieldlib/micro/case/failure.rbMicro::Case::Failure.new/.to_yieldlib/micro/case/with_test_doubles.rbexamples/test_doubles/test/micro/case/success_test.rb,failure_test.rb.to_yieldwrapper behavior, memoised defaultuse_case:test/micro/case/disable_runtime_checks_test.rbWhy opt-in
The factories only make sense in test code. Auto-requiring them adds two new public constants and four module methods to every production process for no production-side benefit. A single
require 'micro/case/with_test_doubles'inspec_helper.rb/test_helper.rbkeeps the constants undefined elsewhere and matches the gem's existingwith_*opt-in convention (cf.with_activemodel_validation).Why under
Micro::Case, not underMicro::Case::Result?The constants sit directly under
Micro::Case(siblings ofMicro::Case::SafeandMicro::Case::Result), not nested underResult. Three reasons:Micro::Case::Success.new(...)reads cleaner thanMicro::Case::Result::Success.new(...). Tests fabricating results are noisy enough without the extra namespace segment.===/deconstruct_keysintegration on top of these constants becomescase result in Micro::Case::Success(slug:)— the shorter path matters most in pattern-match arms.solid-result. Its sibling gem exposesSolid::Success/Solid::Failureat the top level of its namespace, not nested underSolid::Result. Aligning layouts removes one cognitive bump for readers moving between the two libraries.Backward compatibility
Micro::Case::Success/Micro::Case::Failureare new constants —grep -rnconfirms neither existed inlib/ortest/before this PR.Micro::Case::Resultwith their ownSuccess/Failuremodules live at a different namespace path from the new native constants, so the two coexist without collision. Migration is opt-in.Success/Failureinsidecall!. Whenwith_test_doublesis loaded,Micro::Case::SuccessandMicro::Case::Failureexist as constants directly underMicro::Case. Inside aMicro::Casesubclass, a barewordSuccess(no args, no parens) would resolve to the constant, not the producer-side helper method. In practice every realistic call site has arguments (Success(:ok),Success result: {...},Failure :foo), and Ruby parses those as method calls regardless — so this only matters for the contrived case of a method body whose final expression is the literal tokenSuccessorFailure. Both READMEs gain a note explaining the workaround (Success()/Failure()with empty parens).__set_transitionruns as before when transitions are enabled).Micro::Case::Checkentries — the factories piggyback on the existingresult_type!/result_data!/micro_case_instance!guards via__set__.Test plan
bundle exec rake test— 822 runs / 8252 assertions / 0 failures (ENABLE_TRANSITIONS=true)ENABLE_TRANSITIONS=false bundle exec rake test— 561 runs / 7250 assertions / 0 failuresexamples/test_doubles/Minitest+Mocha suite — 8 runs / 28 assertions / 0 failuresexamples/test_doubles/RSpec suite — 8 examples / 0 failuresMicro::Case::Success/Micro::Case::Failureare undefined untilrequire 'micro/case/with_test_doubles', defined after.ENABLE_TRANSITIONS={true,false}).🤖 Generated with Claude Code