-
-
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: make obj
work more like Function
/Class
#12089
Conversation
Did this while looking into #12065. Tested on some of my own (quite large) unittest+pytest projects, and also pytest-django which uses unittest internally (this required adding a backward compat property, but I will update pytest-django if this merged and we could remove the compat). |
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.
Looks great, thanks!
Left minor comments.
src/_pytest/unittest.py
Outdated
# runTest is a special no-op name for unittest, can be used when a dummy | ||
# instance is needed. |
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 don't quite get this comment... "name for unittest" you mean pytest's unittest, or stdlib unittest? Also is this recommending calling runTest to obtain a new dummy instance?
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 will update the comment to make it clearer. Also we don't really need the parameter so I'll omit it.
src/_pytest/unittest.py
Outdated
@@ -63,6 +62,13 @@ class UnitTestCase(Class): | |||
# to declare that our children do not support funcargs. | |||
nofuncargs = True | |||
|
|||
def newinstance(self, method_name: str = "runTest"): |
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 understand we have been using _
to separate words in new methods/functions? Anyway this is murky waters anyway, perhaps you wanted to keep consistency within this class.
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.
This overrides Class.newinstance
so needs the same name
for obj in gc.get_objects(): | ||
assert type(obj).__name__ != "TestCaseObjectsShouldBeCleanedUp" | ||
if type(obj).__name__ == "TestCaseObjectsShouldBeCleanedUp": | ||
assert not hasattr(obj, "an_expensive_obj") |
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.
Where does this an_expensive_obj
comes from? Perhaps leaving a comment explaining it.
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 it's maybe clearer when reading the entire test?
Previously, the `obj` of a `TestCaseFunction` (the unittest plugin item type) was the unbound method. This is unlike regular `Class` where the `obj` is a bound method to a fresh instance. This difference necessitated several special cases in in places outside of the unittest plugin, such as `FixtureDef` and `FixtureRequest`, and made things a bit harder to understand. Instead, match how the python plugin does it, including collecting fixtures from a fresh instance. The downside is that now this instance for fixture-collection is kept around in memory, but it's the same as `Class` so nothing new. Users should only initialize stuff in `setUp`/`setUpClass` and similar methods, and not in `__init__` which is generally off-limits in `TestCase` subclasses. I am not sure why there was a difference in the first place, though I will say the previous unittest approach is probably the preferable one, but first let's get consistency.
Fully understand this change, but this should really have be listed as a breaking change - upgrading pytest caused me to have to track down why collection was failing, and took me a good amount of debugging to find this. |
Previously, the
obj
of aTestCaseFunction
(the unittest plugin item type) was the unbound method. This is unlike regularClass
where theobj
is a bound method to a fresh instance.This difference necessitated several special cases in in places outside of the unittest plugin, such as
FixtureDef
andFixtureRequest
, and made things a bit harder to understand.Instead, match how the python plugin does it, including collecting fixtures from a fresh instance.
The downside is that now this instance for fixture-collection is kept around in memory, but it's the same as
Class
so nothing new. Users should only initialize stuff insetUp
/setUpClass
and similar methods, and not in__init__
which is generally off-limits inTestCase
subclasses.I am not sure why there was a difference in the first place, though I will say the previous unittest approach is probably the preferable one, but first let's get consistency.