-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
unittest: do not use TestCase.debug() with --pdb
#5996
Conversation
834ca0a
to
ae8c158
Compare
ae8c158
to
c7eeb05
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you positive this is the right approach? I ask because the original change was meant for people to be able to debug teardown methods in unittest.TestCase subclasses.
This comment has been minimized.
This comment has been minimized.
--pdb
--pdb
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
c7eeb05
to
cd4f7a4
Compare
Reverts pytest-dev#1890, which needs to be fixed/addressed in another way (pytest-dev#5996). Fixes pytest-dev#5991.
Reverts pytest-dev#1890, which needs to be fixed/addressed in another way (pytest-dev#5996). Fixes pytest-dev#5991.
Reverts pytest-dev#1890, which needs to be fixed/addressed in another way (pytest-dev#5996). Fixes pytest-dev#5991.
=> #6014 for now. |
cd4f7a4
to
216d874
Compare
013a538
to
4b922e5
Compare
I've also tried invoking the pdbinvoke plugin directly from within the wrapped function (simpler in general), but it missing properties that come via |
Fixes pytest-dev#5991 Fixes pytest-dev#3823 Ref: pytest-dev/pytest-django#772 Ref: pytest-dev#1890 Ref: pytest-dev/pytest-django#782 - inject wrapped testMethod - adjust test_trial_error - add test for `--trace` with unittests
4b922e5
to
04f27d4
Compare
Just tried that, but unfortunately if outcome.success:
outcome.expecting_failure = expecting_failure
with outcome.testPartExecutor(self, isTest=True):
self._callTestMethod(testMethod) # <-- call test method
outcome.expecting_failure = False
with outcome.testPartExecutor(self):
self._callTearDown() # <-- call teardown
self.doCleanups()
for test, reason in outcome.skipped:
self._addSkip(result, test, reason)
self._feedErrorsToResult(result, outcome.errors) # <-- call addError, addFailure, etc. So You approach (wrapping the actual method call) seems like the only one that can actually work, thanks for looking at it into such detail. |
- Isolate logic for getting expected exceptions - Use original method name, as users see it when entering the debugger
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot @blueyed, for the research and patience.
While making my comments I was also making the changes myself and testing them, so I decided to just push my changes intead of posting a bunch of diff/suggestions. Feel free to take a look at my comment and accept/reject what you think it is relevant. 👍
Otherwise this LGTM and ready to merge once CI passes.
Thanks again.
src/_pytest/unittest.py
Outdated
|
||
raise _GetOutOf_testPartExecutor(exc) | ||
|
||
self._testcase._wrapped_testMethod = wrapped_testMethod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should override the original method, otherwise the internal name will show up to the user when entering PDB:
self = <foo.T testMethod=_wrapped_testMethod>
def test_foo(self):
self.filename = 'hello'
> assert 0
src/_pytest/unittest.py
Outdated
class _GetOutOf_testPartExecutor(KeyboardInterrupt): | ||
"""Helper exception to get out of unittests's testPartExecutor.""" | ||
|
||
unittest = sys.modules.get("unittest") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point unittest
has definitely been imported, no need to guard against that. 👍
src/_pytest/unittest.py
Outdated
@@ -115,6 +117,8 @@ def setup(self): | |||
self._request._fillfixtures() | |||
|
|||
def teardown(self): | |||
if self._need_tearDown: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure we need this? Even if I comment these two lines, tear down still gets called for this example:
from unittest import TestCase, expectedFailure
class T(TestCase):
def tearDown(self):
print('*********** tearDown')
self.filename = None
@expectedFailure
def test_foo(self):
self.filename = 'hello'
assert 0
λ pytest .tmp\foo.py -sq
*********** tearDown
x
1 xfailed in 0.05s
Uncommenting them actually shows tearDown being called twice:
λ pytest .tmp\foo.py -sq
*********** tearDown
x*********** tearDown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, we actually need it, but only if a test is not an expected failure
@@ -238,17 +238,6 @@ was executed ahead of the ``test_method``. | |||
|
|||
.. _pdb-unittest-note: | |||
|
|||
.. note:: | |||
|
|||
Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Otherwise 'normal' failures won't call teardown explicitly
@@ -217,6 +220,7 @@ def wrapped_testMethod(*args, **kwargs): | |||
expecting_failure = self._expecting_failure(testMethod) | |||
if expecting_failure: | |||
raise | |||
self._needs_explicit_tearDown = True | |||
raise _GetOutOf_testPartExecutor(exc) | |||
|
|||
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it pass #5996 (comment) then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! 👍
Thanks for looking into this! |
Sounds good, but we can consider merging it already (as it solves a bunch of issues and doesn't introduce regressions or external changes), and follow up later with any other changes that improve it. What do you think? |
I'd like to avoid unnecessary diff churn, and think it might be a good opportunity to improve internals, since it shows that it is currently not flexible enough. |
@blueyed I figured you won't be working on this anymore, so decided to re-open it, fix conflicts and merge it. Thanks again! |
Fixes #5991.
/cc @mbyt
TODO: