-
-
Notifications
You must be signed in to change notification settings - Fork 147
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
Feature: descriptive mock assert wrap #58
Feature: descriptive mock assert wrap #58
Conversation
* assert both args and kwargs at the same time for a more detailed report * update tests to assert the above This improves on PR pytest-dev#36 according to the points raised in PR pytest-dev#57
- use unittest.mock for py > 3.3 - small style cleanup (PEP8)
- defined a DETAILED_ASSERTION template and use string formatting - remove redundant print statement - re-raise original AssertionError implicitly
- remove another debugging print
- instead of explicitly unpacking args when we catch the AssertionError we can now simply compare `mock.call` objects, and not just by using the `Mock.assert_X` helper methods. - no need to decode line breaks, look fine when eventually printed - clean up some loose ends
Ok so re-raising the exception implicitly doesn't work (see failed CI run), and raising I opted to writing a custom
I feel this is pretty much ready for review, however I have an issue with the |
Hey @asfaltboy, First of all let me say that I really appreciate all the effort you put into this. 👍 But I have to be honest, I feel a little uneasy on introducing this type of arguably complex code to pytest-mock, plus the current PR uses some internal pytest objects which are not guaranteed to be stable across pytest releases. For those reasons I was hoping we could use the same message raised by pytest's assertion and just augment the original message from I played around a bit and came up with this: def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
return
except AssertionError as e:
__mock_self = args[0]
msg = str(e)
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
try:
assert actual_args == args[1:]
except AssertionError as e:
msg += '\nargs introspection:\n' + str(e)
try:
assert actual_kwargs == kwargs
except AssertionError as e:
msg += '\nkwargs introspection:\n' + str(e)
assert 0, msg Which outputs this:
As you can see unfortunately the assertion introspection messages are duplicated, which I suspect is due to pytest's assertion reinterpretation kicking in... |
Have you tried with the newest |
Hi @nicoddemus thanks for having a look so quick. I know the
|
- fixes failed test `assert_traceback` - also update assertion for simplified introspection
I added some commits to prevent dupe message update, address point 2 above and fix the failing |
Hey @asfaltboy, just stopping by to comment that I'm a little busy at the moment with the pytest-3.0 release. After that is done I plan to take a closer look at your PR! 😅 Again I really appreciate your efforts on this! |
- Unicode characters would fail the assertion otherwise - there may be a better way to do this, but using py.builtin._totext was the easiest I could found without external dependencies - add a test for the unicode scenario (including checking for python version specific (2 and 3) unicode representation)
Using c0382c9 , non-ascii characters in asserted arguments would break pytest-mock in a nasty Unfortunately, I am not aware of any clean way to do this without adding dependencies, so I opted to use Unicode! Phew. |
Hey @asfaltboy, First of all sorry for taking so long! I gave another shot to my patch earlier, which tried to use pytest's own assertion introspection, and discovered what was going on: I updated my patch as follows: def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
return
except AssertionError as e:
if getattr(e, '_mock_introspection_applied', 0):
raise AssertionError(e)
__mock_self = args[0]
msg = str(e)
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
msg += '\n\n... pytest introspection follows:\n'
try:
assert actual_args == args[1:]
except AssertionError as e:
msg += '\nArgs:\n' + str(e)
try:
assert actual_kwargs == kwargs
except AssertionError as e:
msg += '\nKwargs:\n' + str(e)
e = AssertionError(msg)
e._mock_introspection_applied = True
raise e I mark the exception with a Here's the output:
I think this looks like what we wanted, and IMHO very simple. @asfaltboy what do you think? Also, @txomon what do you think of this output? |
This is by far, better than I expected! |
Hi @nicoddemus and thanks for coming back to this. The PR code has duplication prevention test since commit 2296697. I did address your initial concern and removed all external uses for the comparison, excepting the The code in the PR also includes additional features not found in your patch, specifically:
If you think we've cluttered the original issue, I can cleanup this PR to only address the original issue (include original assert and describe args/kwargs), and create new PRs to discuss separately. |
Hey @asfaltboy, sorry for the delay, been really busy lately! I agree with most of what you have said, just some comments below:
Not really sure how useful that is, but on the other hand I don't remember a case where I had to compare I say let's keep
I don't think that's necessary... if you can somehow organize this in separate and clean commits though, it would be great. Also, I think we need to update the Again, sorry for the delay and thanks for all your work on this! |
Hi @nicoddemus thanks for your kind words, and sorry for late response. I'll try my best to find time to work on cleaning up into meaningful commits and adding docs. Meanwhile, I've been using this daily and I sometime encounter odd behavior. |
No worries!
Thanks, take your time!
Which pytest version are you using? There's been some changes regarding |
True! Was using 2.9 something, upgrade to 3.0.3 fixed the issue! |
Sorry for commenting only after so much was already done, but I have a question for you guys. Isn't this proposal more suited for I know it is quicker to implement and have it available in |
Hmmm @fogo has a strong point here. The original functionality was added here mostly because it made sense to re-use pytest's assertion rewriting, but it has grown beyond that and now implements almost all the diff logic itself. I think the entire community can benefit from this instead. What do you think @asfaltboy? |
(EDITED) Sorry for the late response. While it's true that mock users would benefit from this, and I think it's a good idea to open an issue for All the pretty printing of deeply nested dicts and such is The functionality we get out of these two PRs is to have the pretty printing of pytest when asserting mock calls. If we want this in Aha, an issue for better output for mock call asserts has already been raised. I've added a comment in that issue thread. What's really interesting is that the issue demonstrates that the "nice diff output" of unittest might be enough here. I wonder ... Alternatively, if we really don't want this in |
@asfaltboy thanks for the followup. Let's give continue the work on this PR then. As you mention, it is an improvement over #36. Thanks again for all your work and patience! |
@asfaltboy I'm closing this for now so the PR doesn't stay open. Feel free to reopen it when/if you get chance to work on this again. Perhaps it might be a good idea to write a separate plugin for it as well like you suggested. I want to thank you again for all the hard work and interest in contributing to the project! 👍 |
This branch adds original call assertion error message and improves result message. Here is a detailed list of changes:
Attempts to improve on PR #36 according to the points raised in PR #57