An unexpected args error confuses minitest when it looks for the source line #1592
Description
Subject of the issue
We're using rspec-expectations
, rspec-mocks
with minitest
. We're using the minitest_integration
with both of them. For the most part everything works well. But sometimes failures cause minitest's location logic to not find where the problem really occurred.
For example, we have a test like:
class ExampleTest
test "something" do
expect(a_real_object).to receive(:a_method).with("these", "args")
do_something
end
end
And if do_something
eventually results in a call to a_real_object.a_method
, but with different arguments. We'll get failure output like:
Failure:
ExampleTest#test_something [/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/proxy.rb:217]:
But that source location isn't very helpful. I traced it down to the way minitest looks for a probable failure line. It looks at the backtrace of the exception and grabs the line just after a line matching the regex /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
(note, this is based on the version of minitest I'm using, but the most recent version does essentially the same thing).
And the backtrace looks like:
"/Users/user/.gem/ruby/2.7.8/gems/rspec-support-3.12.0/lib/rspec/support.rb:102:in `block in <module:Support>'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-support-3.12.0/lib/rspec/support.rb:111:in `notify_failure'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/error_generator.rb:348:in `notify'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/error_generator.rb:332:in `__raise'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/error_generator.rb:55:in `raise_unexpected_message_args_error'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/message_expectation.rb:555:in `raise_unexpected_message_args_error'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/proxy.rb:217:in `message_received'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/proxy.rb:361:in `message_received'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/method_double.rb:91:in `proxy_method_invoked'",
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/method_double.rb:67:in `block (2 levels) in define_proxy_method'",
"/Users/user/project/lib/example.rb:10:in `do_something'",
"/Users/user/project/test/unit/lib/example_test.rb:4:in `block in <class:ExampleTest>'",
"/Users/user/.gem/ruby/2.7.8/gems/minitest-5.11.3/lib/minitest/test.rb:98:in `block (3 levels) in run'",
"/Users/user/.gem/ruby/2.7.8/gems/minitest-5.11.3/lib/minitest/test.rb:195:in `capture_exceptions'"
So this line gets matched by the regex:
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/message_expectation.rb:555:in `raise_unexpected_message_args_error'",
And then this line is the next one, where minitest thinks the error is:
"/Users/user/.gem/ruby/2.7.8/gems/rspec-mocks-3.12.5/lib/rspec/mocks/proxy.rb:217:in `message_received'",
I feel like it would be more correct for the location of the error to be:
"/Users/user/project/lib/example.rb:10:in `do_something'",
Is there some way rspec-mocks
could create an exception with a backtrace that leads to the project's code rather than itself?
FYI: The actual exception being raised is a Minitest::Assertion
, but it was created by rspec-mocks
as a RSpec::Mocks::MockExpectationError
. I'm guessing it's the minitest_integration
that sets that class to Minitest::Assertion
.
Your environment
- Ruby version: 2.7.8
- rspec-mocks version: 3.12.5
- rspec-expectations version: 3.12.2
- rspec version: 3.12.0