Closed onlynone closed 2 months ago
:wave: This is up to minitest, we use Ruby semantics when it comes to raises errors or allowing errors to be raised. In rspec-core
we filter backtraces before displaying them so this would be the responsibility of minitest
here.
For that to work, wouldn't minitest need to have detailed backtrace filtering heuristics for any third-party testing tool that might raise errors from deep within their own code? Minitest already does its own filtering of backtraces to weed out its own code, but after that it just looks to where the raise happened.
It seems like it could be pretty helpful if rspec could craft a backtrace that pointed to where the mock actually received the bad call.
Gem names create predictable filenames, in rspec-core
we offer even an api to filter 3rd party gems from our display, we do filter out ourselves slightly differently in that we fillter out a list of lib/rspec/<gem>
but you could actually use our filter_gem
functionality to achieve the same. This is much less maintenance work than trying to do this at raise time, and although I've not benchmarked it I would guess its more performant.
We may be misunderstanding each other.
I think the problem is that rspec isn't the one displaying the backtraces, minitest is. That library looks through the backtrace to see if they can find where something was raised or asserted. To that library, there's no difference between an exception being raised in rspec's code (or any other random assertion/expectation library) and user code.
However, when rspec creates the exception, it could pre-remove its own lines from the exception's backtrace so that the first line would point to where the problem happened in the user's code.
I created a PR to show what I mean in #1593 .
We just solve this problem with a feature to filter gems out which are considered 3rd party, it could be rspec-mocks, or shoulda, or any other library. Minitest could also implement a feature like this.
Removing the backtrace lines would duplicate our logic for filtering out lines when displaying them, and also circumvent our feature which allows you to optionally remove that filtering and see the entire stacktrace, so changing this behavious is not something we want to do.
We just solve this problem with a feature to filter gems out which are considered 3rd party
Is there a way to leverage the existing code in rspec-core to do this then?
I feel like there's not a lot of duplication since the issue I'm having isn't about filtering out arbitrary third-party gems. It's just having rspec-mocks remove its own lines from backtraces. Since rspec-mocks is where the exception is created, it seems like it would be the best and most direct place to deal with removing the extra lines.
also circumvent our feature which allows you to optionally remove that filtering and see the entire stacktrace
That sounds like an essential feature, I think it would make sense to make the type of filtering I'd like configurable as well. Is there some way you could see that rspec-mocks could leverage all the existing code and configuration from rspec-core to do this. Or could the feature I'd like to see added to rspec-mocks be placed behind its own option?
I'm viewing this similar to how mock expectations that don't fire a failure until teardown will set the backtrace_line
so that developers can see where their code where the expectation was created, since it doesn't really help to see that an exception was raised in teardown
/ verify
. This is a situation where rspec-mocks
helps out the developer by mucking with the backtrace to put the troublesome line at the top. I was just hoping to do the same for expectations that fail during the test instead of after.
Subject of the issue
We're using
rspec-expectations
,rspec-mocks
withminitest
. We're using theminitest_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:
And if
do_something
eventually results in a call toa_real_object.a_method
, but with different arguments. We'll get failure output like: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:
So this line gets matched by the regex:
And then this line is the next one, where minitest thinks the error is:
I feel like it would be more correct for the location of the error to be:
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 byrspec-mocks
as aRSpec::Mocks::MockExpectationError
. I'm guessing it's theminitest_integration
that sets that class toMinitest::Assertion
.Your environment