In Minitest 6, how are plugins and reporters loaded, and why does a suite's --pride flag or custom reporter stop working after the upgrade?
answer
- plugin loading is opt-in since 6.0
- minitest/<name>_plugin.rb
- plugin_<name>_options and plugin_<name>_init
- Minitest.load :pride / load_plugins
- AbstractReporter: start, record, report, passed?
basics
~10 sMinitest 6 no longer auto-loads every installed minitest/*_plugin.rb. Require the plugin in the test helper, call Minitest.load :name, or call Minitest.load_plugins; reporters are added to Minitest.reporter from a plugin's init hook.
solid answer
~40 sA Minitest plugin is a file named `minitest/<name>_plugin.rb` that defines `Minitest.plugin_<name>_options(opts, options)` to add command-line flags and `Minitest.plugin_<name>_init(options)` to act on them, usually by adding a reporter with `self.reporter << MyReporter.new(options)`. Up to Minitest 5 every such file found by RubyGems was loaded automatically. **Minitest 6.0 made plugin loading opt-in**: until you `require` the plugin, call `Minitest.load :pride`, or call `Minitest.load_plugins` to restore the old scan, its options do not exist, so `--pride` is an invalid option and a reporter gem does nothing. Reporters subclass `Minitest::AbstractReporter` and override `start`, `record(result)`, `report` and `passed?`; the default composite already holds a `SummaryReporter` and a `ProgressReporter`.
code
ruby · 9 lines# test/test_helper.rb
require "minitest/autorun"
require "minitest/pride" # loads and enables the pride plugin
# or register by name so its flags exist:
# Minitest.load :pride
# or restore Minitest 5's scan of every installed plugin:
# Minitest.load_pluginsgo deeper
Recall that Minitest 6 plugins must be loaded explicitly, for example with require "minitest/pride", before their flags work.
Explain the plugin file and hook naming, Minitest.load versus load_plugins, and the reporter methods start, record, report and passed?.
Diagnose missing output or invalid options after an upgrade, and write a reporter plugin for CI that is loaded explicitly.
Argue for explicit plugin loading as a reproducibility rule, and decide which reporters a shared test helper should carry.
## What a plugin is Minitest's extension mechanism is a naming convention: - A file named **`minitest/<name>_plugin.rb`** somewhere on the load path (usually inside a gem). - An optional **`Minitest.plugin_<name>_options(opts, options)`**, called while Minitest builds its `OptionParser`, to register flags. - An optional **`Minitest.plugin_<name>_init(options)`**, called after options are parsed, to set things up, typically by adding a reporter. A plugin can also be a module registered with `Minitest.register_plugin`, answering `minitest_plugin_options` and `minitest_plugin_init`. ## The 6.0 change: opt-in loading Minitest 5 scanned every installed gem for `minitest/*_plugin.rb` at start-up and loaded them all. That made test runs depend on whatever happened to be installed. Minitest 6.0 stopped doing it. The History file lists three replacements: 1. **Require the plugin** in the test helper, for example `require "minitest/pride"`, which loads the pride plugin and switches it on. 2. **`Minitest.load :pride`**, which requires `minitest/pride_plugin` and registers it by name, so its options (such as `-p`/`--pride`) exist. 3. **`Minitest.load_plugins`**, which restores the old behaviour by requiring every `minitest/*_plugin.rb` that RubyGems can find. The `--no-plugins` flag is now a no-op that only prints a warning, and the old `minitest/manual_plugins` file only warns that it is no longer necessary. ## Symptoms after upgrading | symptom | cause | fix | |---|---|---| | `invalid option: --pride`, usage printed, exit 1 | the pride plugin's options were never registered | `require "minitest/pride"` or `Minitest.load :pride` | | a reporter gem's output disappears | its `*_plugin.rb` is no longer loaded | require it, or call `Minitest.load_plugins` | | CI annotations or JUnit-style files stop appearing | same | same | The load must happen before `Minitest.run` parses options, which in practice means in the test helper next to `require "minitest/autorun"`. ## Reporters Every result flows through `Minitest.reporter`, a `CompositeReporter` that by default contains a `SummaryReporter` (the header, failure details and totals) and, unless `--quiet`, a `ProgressReporter` (the dots, or names and times with `-v`). A custom reporter subclasses `Minitest::AbstractReporter` (or `StatisticsReporter` to get counts) and overrides: - `start`: the run is starting. - `prerecord(klass, name)`: a test is about to run. - `record(result)`: one test finished; `result.result_code` is `.`, `F`, `E` or `S`. - `report`: the run finished; write the summary or file. - `passed?`: whether this reporter considers the run green. It joins the composite from a plugin's init hook: ```ruby # lib/minitest/fines_plugin.rb module Minitest def self.plugin_fines_options(opts, options) opts.on "--fines-json PATH", "Write results as JSON" do |path| options[:fines_json] = path end end def self.plugin_fines_init(options) self.reporter << FinesJsonReporter.new(options[:fines_json]) if options[:fines_json] end end ``` On Minitest 6 the suite still has to opt in with `Minitest.load :fines` or a require. ## Checking what is loaded Run the suite with `--help`. When any plugin is registered, the usage text ends with a `Known extensions:` line naming them, followed by the flags each plugin added. If the line is missing, or a plugin you expected is not in it, that plugin was never loaded, and its flags will be rejected as invalid options. This is the fastest check after an upgrade, and it works the same through `ruby`, `rake test A=--help` or the `minitest` command. ## Why this design Explicit loading makes a run reproducible from its own code: the same Gemfile and helper produce the same options and reporters on every machine, instead of changing when someone installs an unrelated gem. The cost is one line per plugin in the helper.
- In Minitest 6, what is the risk of simply calling Minitest.load_plugins everywhere?It brings back Minitest 5's behaviour: every `minitest/*_plugin.rb` any installed gem ships is required, so options and reporters depend on the machine's installed gems rather than the project's code. Loading named plugins keeps runs reproducible.
- In a Minitest reporter, which method sees every test result, and what does it receive?`record(result)`, called once per test after it runs. The result carries the test's name, class, time, assertion count and failures, and `result_code` returns `.`, `F`, `E` or `S`. `report` is called once at the end to write the summary.
saying these in an interview costs you the question
- Minitest 6 still loads every installed plugin automatically.
- --no-plugins is needed to stop plugins loading in Minitest 6.
- A reporter must replace SummaryReporter rather than join the composite.
- Plugins can be loaded after Minitest.run has parsed the options.
- --pride is a built-in option available without loading anything.