Skip to content

Commit f824429

Browse files
committed
Fix minitest reporter crash with parallel testing (#3602)
### Motivation Fix crash when using ActiveSupport's parallel testing with the Minitest reporter. When running tests in parallel, ActiveSupport passes a `PrerecordResultClass` wrapper (a Struct containing only the class name) instead of the actual test class, since class objects cannot be marshaled across process boundaries via DRb. ### Implementation Updated the `prerecord` method in MinitestReporter to handle both regular test classes and ActiveSupport's wrapper objects: - Renamed parameter from `test_class` to `test_class_or_wrapper` to clarify it can receive different types - Extract the class name using `.name` (which both real classes and the `PrerecordResultClass` wrapper respond to) - Attempt to resolve the actual class using `Object.const_get` - If `const_get` fails (e.g., for Minitest specs with invalid constant names like `"MySpec::when something is true"`), use the original object since it's already the test class - Continue with the existing logic using the resolved class This approach handles three scenarios: - Regular test classes: `const_get` successfully resolves the class - ActiveSupport's PrerecordResultClass: Contains the class name as a string, `const_get` retrieves the actual class from the main process - Minitest specs with describe blocks: `const_get` fails due to invalid constant names (with spaces), but we already have the class object ### Manual Tests To test these changes: 1. Open a Rails project that uses ActiveSupport's parallel testing 2. Run tests using the Ruby LSP test runner 3. Verify that test results are correctly reported in the Test Explorer view
1 parent be39d56 commit f824429

File tree

1 file changed

+17
-4
lines changed

1 file changed

+17
-4
lines changed

lib/ruby_lsp/test_reporters/minitest_reporter.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,25 @@ def minitest_plugin_init(_options)
7474
end
7575
end
7676

77-
#: (singleton(Minitest::Test) test_class, String method_name) -> void
78-
def prerecord(test_class, method_name)
79-
uri, line = LspReporter.instance.uri_and_line_for(test_class.instance_method(method_name))
77+
#: (untyped, String) -> void
78+
def prerecord(test_class_or_wrapper, method_name)
79+
# In frameworks like Rails, they can control the Minitest execution by wrapping the test class
80+
# But they conform to responding to `name`, so we can use that as a guarantee
81+
# We are interested in the test class, not the wrapper
82+
name = test_class_or_wrapper.name
83+
84+
klass = begin
85+
Object.const_get(name) # rubocop:disable Sorbet/ConstantsFromStrings
86+
rescue NameError
87+
# Handle Minitest specs that create classes with invalid constant names like "MySpec::when something is true"
88+
# If we can't resolve the constant, it means we were given the actual test class object, not the wrapper
89+
test_class_or_wrapper
90+
end
91+
92+
uri, line = LspReporter.instance.uri_and_line_for(klass.instance_method(method_name))
8093
return unless uri
8194

82-
id = "#{test_class.name}##{handle_spec_test_id(method_name, line)}"
95+
id = "#{name}##{handle_spec_test_id(method_name, line)}"
8396
LspReporter.instance.start_test(id: id, uri: uri, line: line)
8497
end
8598

0 commit comments

Comments
 (0)