Fix must_*/wont_* expectations raising NoMethodError on Minitest 6 - #887
Open
hikmetba-bit wants to merge 1 commit into
Open
hikmetba-bit wants to merge 1 commit into
hikmetba-bit wants to merge 1 commit into
Conversation
minitest_spec.rb called AASM.infect_an_assertion(...) for each
must_*/wont_* pair. infect_an_assertion only ever defines the
generated method as an instance method on whatever module it's
called on. Under Minitest 5.x, calling it on an arbitrary module
(AASM here) also happened to define the real method on
Minitest::Expectation - the class that `_(obj).must_foo` /
`value(obj).must_foo` actually dispatch through - via a second,
explicitly-deprecated code path ("This will fail in Minitest 6").
That path was removed in Minitest 6.0, so AASM.infect_an_assertion
alone stopped having any effect on that usage, and every
must_*/wont_* assertion raised:
NoMethodError: undefined method 'must_have_state' for an instance
of Minitest::Expectation
Call infect_an_assertion on both AASM (preserving whatever the
Minitest 5.x deprecated global mechanism still provides, e.g. bare
`obj.must_foo` - not something restorable here since Minitest 6.0
itself intentionally removed that mechanism) and directly on
Minitest::Expectation, which is what actually needs the method
defined on it and works correctly on both Minitest 5.x and 6.x.
Added a regression test suite using the `_(obj).must_foo` wrapper -
the form Minitest's own deprecation warning already recommends -
covering all four matcher pairs. The existing bare-call tests
elsewhere in this file remain as-is; they depend on Minitest's own
removed global-expectations mechanism and are a separate, broader
Minitest 6 migration concern beyond this fix's scope.
Fixes aasm#875.
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
Fixes #875.
minitest_spec.rbcalledAASM.infect_an_assertion(...)for eachmust_*/wont_*pair.infect_an_assertiononly ever defines the generated method as an instance method on whatever module it's called on. Under Minitest 5.x, calling it on an arbitrary module (AASMhere) also happened to define the real method onMinitest::Expectation— the class that_(obj).must_foo/value(obj).must_fooactually dispatch through — via a second code path that Minitest 5.x's own source explicitly flags as deprecated ("This will fail in Minitest 6"). That path was removed in Minitest 6.0, soAASM.infect_an_assertionalone stopped having any effect on that usage, and everymust_*/wont_*assertion raised:exactly matching the issue report.
Fix
Call
infect_an_assertionon bothAASM(preserving whatever Minitest 5.x's deprecated global mechanism still provides there, e.g. bareobj.must_foo— not something restorable here since Minitest 6.0 itself intentionally removed that mechanism, it's not AASM-specific) and directly onMinitest::Expectation, which is what actually needs the method defined on it and works correctly on both Minitest 5.x and 6.x.Scope note
This fixes the
_(obj).must_foo/value(obj).must_fooform — the one Minitest's own deprecation warning recommends migrating to, and the one the reportedNoMethodErroris actually about (note it namesMinitest::Expectationas the receiver, which only happens via that wrapped form). The bareobj.must_fooform used throughout the rest of this test file (and the README) depends on Minitest's own removed global-expectations mechanism and remains unavailable under Minitest 6 — that's a separate, broader migration Minitest itself forced on every one of its users, not something fixable from AASM's side without reintroducing globalObjectpollution. Flagging it here for visibility rather than trying to paper over it.Verification
Reproduced the exact reported crash: installed real Minitest 6.0.6 and 5.25.4 in isolated Gemfiles, confirmed
_(obj).must_have_state(...)raises the identicalNoMethodErroron 6.0.6 with the code as-is onmain, and that it's fixed after this change — while the existing bare-call tests keep passing unchanged on 5.25.4 (with their pre-existing deprecation warnings, unaffected by this diff) and remain in their pre-existing (unrelated, not regressed) broken state on 6.0.6.Added a regression describe block to
test/unit/minitest_matcher_test.rbcovering all four matcher pairs via the_(obj)wrapper. Mutation-tested: reverting the source change makes all 12 tests in the file fail under Minitest 6.0.6 (including the 4 new ones, with the exact reported error); with the fix, the 4 new tests pass under both 5.25.4 and 6.0.6, and the full file remains 12/12 passing under 5.25.4 (0 regressions).🤖 Generated with Claude Code