-
-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-36871: Handle spec errors in assert_has_calls #16005
Conversation
The fix in PR 13261 handled the underlying issue about the spec for specific methods not being applied correctly, but it didn't fix the issue that was causing the misleading error message. The code currently grabs a list of responses from _call_matcher (which may include exceptions). But it doesn't reach inside the list when checking if the result is an exception. This results in a misleading error message when one of the provided calls does not match the spec.
Ah, wait, I made my fix to the wrong line! Also, assert_has_awaits has the exact same bug. |
As well as assert_has_awaits, where I misplaced that fix previously. Add tests for both fixes.
This error handling bug isn't a huge problem most of the time. But when there's a bug in the creation or application of the mock specs (like the bugs fixed in earlier PRs for this issue), the resulting error message is really confusing, since it incorrectly says that the expected calls were not present, even when the expected and actual calls are in fact identical. |
It's a PEP currently being written, it would help handle multiple exceptions perfectly. It is interesting |
I think first makes a little more sense. The first exception encountered is the one that changes the state from passing to failing, and it's the one that would be raised if handling of those exceptions was not deferred. |
Include all exceptions deferred when binding expected calls with the spec in the error messages generated by assert_has_calls and assert_has_awaits. To make the origin of each exception clear, the exceptions are presented in a list parallel to the expected calls, with None at positions where processing the call generated no exception. The first exception encountered is still recorded as the cause of the AssertionError, since that's the exception which would have been raised had the handling of those exceptions not been deferred.
Is this ready to merge? |
I think this is ready to merge, but can you use blurb to add a news entry about the fixed error message? It's worth calling out. |
(Thought the blurb-it tool would handle that.)
@sfreilich: Status check is done, and it's a failure ❌ . |
I'm having trouble backporting to |
Sorry, @sfreilich, I could not cleanly backport this to |
I'll create another PR for that, sorry. |
No problem. Please also add a test too with the PR as below which would fail without the error message change suggested. Normally we don't added very specific asserts for error message but I think it helps in this case. I think with self.assertRaisesRegex(
AssertionError,
re.escape("Error processing expected calls.\n"
"Errors: [None, TypeError('too many positional "
"arguments')]\n"
"Expected: [call(), call('wrong')]")) as cm:
mock.assert_has_calls([call(), call('wrong')])
with self.assertRaisesRegex(
AssertionError,
re.escape("Error processing expected calls.\n"
"Errors: [None, TypeError('too many positional "
"arguments')]\n"
"Expected: [call(), call('wrong')]\n"
"Actual: [call()]")) as cm:
mock()
mock.assert_has_calls([call(), call('wrong')]) |
PR #16361 |
Thanks @sfreilich for the PR 🌮🎉.. I'm working now to backport this PR to: 3.8. |
Sorry @sfreilich, I had trouble checking out the |
so much for automation. i'll take care of the backports of both of these PRs. |
Thanks @sfreilich for the PR 🌮🎉.. I'm working now to backport this PR to: 3.8. |
GH-16364 is a backport of this pull request to the 3.8 branch. |
…H-16364) The fix in PR 13261 handled the underlying issue about the spec for specific methods not being applied correctly, but it didn't fix the issue that was causing the misleading error message. The code currently grabs a list of responses from _call_matcher (which may include exceptions). But it doesn't reach inside the list when checking if the result is an exception. This results in a misleading error message when one of the provided calls does not match the spec. https://bugs.python.org/issue36871 Automerge-Triggered-By: @gpshead (cherry picked from commit b5a7a4f) Co-authored-by: Samuel Freilich <[email protected]> https://bugs.python.org/issue36871 Automerge-Triggered-By: @gpshead
I am not sure of the backport to 3.7 . Though this fixes the bug in detecting Exception it also enhances and changes error message which some package might be asserting in their test. IMO we can backport only exception detecting code if needed to 3.7 instead of error message enhancement. |
The specific error message produced by unittest.mock.Mock.assert_has_calls seems like a strange thing to be asserting about in tests. Unless the test is testing a subclass of mock.Mock or something. Basically, I see your concern, but I think the tradeoffs are less towards interface stability when the error messages in question are generated by test assertions. That said, the more important part is the one that prevents the exceptions from spec.bind from being swallowed entirely and ensures at lest the first one ends up reported to the user somehow (e.g. by setting it as the cause of the AssertionError). |
There are packages that assert exact error messages that are mostly built on top of mock module. Sample issue over 3.8 error message alignment change : pytest-dev/pytest-mock#139
Okay, I see it as a partly improvement in error message with the bug fix to detect the exception object. I will leave it to the core dev for the backport to 3.7 decision. |
…) (pythonGH-16364) The fix in PR 13261 handled the underlying issue about the spec for specific methods not being applied correctly, but it didn't fix the issue that was causing the misleading error message. The code currently grabs a list of responses from _call_matcher (which may include exceptions). But it doesn't reach inside the list when checking if the result is an exception. This results in a misleading error message when one of the provided calls does not match the spec. https://bugs.python.org/issue36871 Co-authored-by: Samuel Freilich <[email protected]> (cherry picked from commit 1a17a05)
PR #16374 has a backport to 3.7. I see the incorrect error message as a bug so I'm leaning towards merging it. That pytest has some of its own test suite depend on specifics of error message isn't super surprising given the nature of pytest, but it is within its own test suite and they seem to understand how to fix fragile golden value data issues in those tests if/when they do come up. I wouldn't expect any normal actual user code to reasonably depend on specific formatting of error messages from mock assertions. |
…H-16374) Handle spec errors in assert_has_calls (GH-16005) (GH-16364) The fix in PR 13261 handled the underlying issue about the spec for specific methods not being applied correctly, but it didn't fix the issue that was causing the misleading error message. The code currently grabs a list of responses from _call_matcher (which may include exceptions). But it doesn't reach inside the list when checking if the result is an exception. This results in a misleading error message when one of the provided calls does not match the spec. https://bugs.python.org/issue36871 Co-authored-by: Samuel Freilich <[email protected]> (cherry picked from commit 1a17a05)
The fix in PR #13261 handled the underlying issue about the spec for specific methods not being applied correctly, but it didn't fix the issue that was causing the misleading error message.
The code currently grabs a list of responses from _call_matcher (which may include exceptions). But it doesn't reach inside the list when checking if the result is an exception. This results in a misleading error message when one of the provided calls does not match the spec.
https://bugs.python.org/issue36871
Automerge-Triggered-By: @gpshead