With Minitest 6 on Ruby 4.0, why does require "minitest/mock" raise LoadError, and how do you get Minitest::Mock and Object#stub back?
answer
- Minitest 6.0 dropped a file
- extracted in 5.27.0.beta.1
- gem name: minitest-mock
- not a Ruby bundled gem
- require still says minitest/mock
basics
~10 sMinitest 6.0 removed minitest/mock.rb and moved it into the separate minitest-mock gem, which Ruby 4.0 does not bundle. Add minitest-mock to the test dependencies; the require path, Minitest::Mock and Object#stub stay the same.
solid answer
~40 sUp to Minitest 5, `Minitest::Mock`, `Object#stub` and `assert_mock` lived in `minitest/mock.rb` inside the minitest gem. Minitest 6.0.0 dropped that file, and the code now ships as the **minitest-mock** gem (extracted in 5.27.0.beta.1). Ruby 4.0.7 bundles Minitest 6.0.0 but not minitest-mock, so `require "minitest/mock"` raises `LoadError` (`cannot load such file -- minitest/mock`) until the gem is installed and, under Bundler, listed in the Gemfile's test group. Nothing else changes: the require path is still `"minitest/mock"`, and `Minitest::Mock`, `MockExpectationError`, `Object#stub` and `assert_mock` behave as before. Require it after `minitest/autorun` in the test helper, since it adds the `must_verify` expectation only when Minitest's spec DSL is already loaded.
code
ruby · 9 lines# Gemfile
group :test do
gem "minitest", "~> 6.0"
gem "minitest-mock"
end
# test/test_helper.rb
require "minitest/autorun"
require "minitest/mock" # same path as before, now served by minitest-mockgo deeper
Recall that Minitest 6 moved Minitest::Mock and Object#stub into the minitest-mock gem, and that the require line itself stays require "minitest/mock".
Explain why Ruby 4.0's bundled Minitest 6 triggers the LoadError and why suites that only call stub break as well.
Show how you would catch this in an upgrade: explicit pins, a lockfile diff review, and a helper that fails loudly at load time.
Use this as a case for pinning every test dependency explicitly, so a language upgrade never silently changes the test framework's major version.
## What moved Minitest has always shipped a small mocking library written by Steven Baker: the `Minitest::Mock` class, the `MockExpectationError` exception, `Object#stub`, and the `assert_mock` assertion (with its spec form `must_verify`). Until Minitest 5 it was the file `lib/minitest/mock.rb` inside the **minitest** gem, loaded on demand with `require "minitest/mock"`. Minitest 6.0.0 (December 2025) lists "Dropped minitest/mock.rb. This has been extracted to the minitest-mock gem" among its removals. The new gem's first release was 5.27.0.beta.1, and it continues the 5.27 version line: the current release is **minitest-mock 5.27.0**, which requires Ruby 3.1 or later. ## Why a Ruby 4.0 project notices first Ruby 4.0.7 ships Minitest 6.0.0 as a **bundled gem**, so a project that never pinned Minitest picks up 6.x with the new Ruby. minitest-mock is not on Ruby's bundled-gem list. The result, in a test file that has worked for years: ```ruby require "minitest/autorun" require "minitest/mock" # LoadError: cannot load such file -- minitest/mock ``` The failure happens at load time, so the whole test file (or the whole suite, if the require sits in a shared helper) fails before a single test runs. ## The fix 1. Add the gem to the project's test dependencies: `gem "minitest-mock"` in the Gemfile's `:test` group (or `bundle add minitest-mock`), then install. 2. Keep the require exactly as it was: `require "minitest/mock"`. The gem kept the file path, so no test code changes. 3. Load it after Minitest itself, typically in `test/test_helper.rb` right after `require "minitest/autorun"`, because it adds `assert_mock` to `Minitest::Assertions` and defines `must_verify` only if the spec DSL is already loaded. ## What did not change | API | still there | notes | |---|---|---| | `Minitest::Mock.new` | yes | optional delegator argument | | `#expect` / `#verify` | yes | same signatures | | `MockExpectationError` | yes | a top-level constant, subclass of `StandardError` | | `Object#stub` | yes | defined on `Object` by the same file | | `assert_mock` / `must_verify` | yes | added to `Minitest::Assertions` and `Minitest::Expectations` | So the migration is a dependency change, not a code change. ## Related traps - **Guessing a new path.** `require "minitest-mock"` fails: the gem's only library file is still `minitest/mock.rb`. - **Assuming `stub` is core Minitest.** `Object#stub` is defined in the same file, so a suite that uses only `stub` and never `Minitest::Mock` breaks too, usually as `NoMethodError: undefined method 'stub'` if the require was removed rather than fixed. - **Pinning Minitest back to 5.x forever.** That works today, but Minitest 5.27.0 announced the end of life of the 5 series; adding one gem is the smaller change. - **Treating it as a Bundler bug.** Outside Bundler, `gem install minitest-mock` is enough; inside it, a gem that is not in the Gemfile cannot be required, which is the intended behaviour. ## Checking what a suite actually loads Two quick checks settle which versions a test run uses: - `bundle exec ruby -e 'require "minitest"; puts Minitest::VERSION'` prints the Minitest version Bundler resolves for the project, which is what the tests get. - `bundle exec ruby -e 'require "minitest/mock"; puts Minitest::Mock::VERSION'` succeeds only when minitest-mock is available, and prints its version. Running the same commands without `bundle exec` shows what the Ruby installation alone would provide, which is where the bundled Minitest 6.0.0 appears. ## How to spot it in a log The signature is a `LoadError` naming `minitest/mock`, raised from a test helper, right after an upgrade of Ruby or Minitest. The Gemfile.lock diff shows `minitest (6.x)` and no `minitest-mock` entry.
- With Minitest 6, a suite uses only Object#stub and never Minitest::Mock. Does it still need minitest-mock?Yes. `Object#stub` is defined in the same `minitest/mock.rb` file that moved to the minitest-mock gem, alongside `Minitest::Mock`, `MockExpectationError` and `assert_mock`. Without the gem there is no `stub` method on objects, so the tests fail with `NoMethodError`, or earlier with `LoadError` if the require is still there.
- Why does the upgrade to Ruby 4.0 often surface this LoadError even when nobody touched Minitest?Ruby 4.0 bundles Minitest 6.0.0. A project that relied on the Ruby-provided Minitest, or listed it without a version constraint, resolves to 6.x after the Ruby upgrade, and 6.x no longer contains `minitest/mock.rb`. Pinning versions explicitly makes such changes visible in the lockfile.
saying these in an interview costs you the question
- Minitest 6 renamed the require path to minitest-mock.
- Object#stub is part of core Minitest 6, so only Mock users are affected.
- Ruby 4.0 bundles minitest-mock alongside minitest.
- Minitest::Mock was removed in 6.0 and has no replacement.
- The only fix is to pin Minitest back to 5.x.