-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Integrate pytest-subtests #13738
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
base: main
Are you sure you want to change the base?
Integrate pytest-subtests #13738
Conversation
I agree that this feature should be in pytest core because it's a feature in unittest and pytest should aim to be a drop in replacement for unittest (plus the original issue have 40 👍 and no 👎 at time of writing, overwhelming popular support in my book). |
I recall we need to fix marking the owning test case as failed if one subtest fails |
But yeah i want to see this in |
Sounds reasonable |
b43ab38
to
97ee032
Compare
97ee032
to
6b5831f
Compare
5f56d81
to
c93c0e0
Compare
Ready for an initial review folks. |
In addition, enable the plugin in `pytest/__init__.py` and `config/__init__.py`.
c93c0e0
to
b569c93
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.
Nice to see this happening.
I ran out of time for the review for today, so didn't really get to the implementation parts, but already have some comments so submitting a partial review.
from _pytest.runner import check_interactive_exception | ||
|
||
|
||
if TYPE_CHECKING: |
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.
No need for TYPE_CHECKING
for these imports
) | ||
|
||
|
||
@dataclasses.dataclass |
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.
Can this class be immutable? I.e. frozen=True, and dict -> Mapping.
Can also consider slots=True
, and kw_only=True
(which is nice for backward compat).
|
||
# Note: cannot use a dataclass here because Sphinx insists on showing up the __init__ method in the documentation, | ||
# even if we explicitly use :exclude-members: __init__. | ||
class SubTests: |
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.
Consider spelling this Subtests
. This way it matches the fixture name subtests
(rather than sub_tests
). Generally the sub- prefix is not followed by a capital letter AFAIK. I understand it's inconsistent with unittest spelling subTest
but that's on them :)
Note I don't feel strongly about this, if you prefer SubTests that's fine.
|
||
# Note: cannot use a dataclass here because Sphinx insists on showing up the __init__ method in the documentation, | ||
# even if we explicitly use :exclude-members: __init__. | ||
class SubTests: |
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.
Another naming point, for some fixture types we use the Fixture
suffix, e.g. CaptureFixture
. But for some we don't (e.g. MonkeyPatch
, Cache
, Pytester
). Perhaps the difference is whether the class only makes sense in a fixture context (like CaptureFixture
) or if it's usable independently. For SubTests
I believe it's strictly a fixture, so that would suggest SubTestsFixture
. But that's a bit longer.
I wish we were consistent on this. I'll leave the decision to your good judgment.
class SubTests: | ||
"""Subtests fixture, enables declaring subtests inside test functions via the :meth:`test` method.""" | ||
|
||
def __init__( |
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.
Add _ispytest
privacy check?
|
||
pytest allows for grouping assertions within a normal test, known as *subtests*. | ||
|
||
Subtests are an alternative to parametrization, particularly useful when test setup is expensive or when the exact parametrization values are not known at collection time. |
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.
For expensive test setup, pytest has a solution in the form of scoped fixtures. Since regular parametrization is advantageous, e.g. allowing each param value to be tested individually by nodeid, I think we should not encourage subtests when there's a decent parametrization alternative.
The "values not known at collection time" case is what I think we should emphasize. There is indeed inherently no way to do "dynamic parametrization" after collection.
) | ||
|
||
|
||
class TestSubTest: |
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.
class TestSubTest: | |
class TestUnittestSubTest: |
|
||
|
||
class TestSubTest: | ||
"""Test.subTest functionality.""" |
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.
"""Test.subTest functionality.""" | |
"""Test subTest() functionality in unittest tests.""" |
Unless I misunderstood.
|
||
|
||
@pytest.mark.parametrize("mode", ["normal", "xdist"]) | ||
class TestFixture: |
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.
Would be nice to have a test for the combination of parametrization + subtests. I'm sure it works, but it's the sort of thing that I can see might break if a different implementation strategy was used.
--------------------------- | ||
|
||
While :ref:`traditional pytest parametrization <parametrize>` and ``subtests`` are similar, they have important differences and use cases. | ||
|
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.
Continuing from the comment above, I can see two ways we can approach this:
1 - Subtests are for "runtime parametrization"
Per comment above, subtests are useful when you have some data dynamically fetched in the test and want individualized reporting for each data value.
The idea here is that parametrization should be the go-to tool, but we offer this subtest tool for this particular scenario.
2 - Subtests are for "sub-testing"
By which I mean, subtests are for when you have one conceptual test, i.e. consider it a complete whole, but just want to break down its reporting to parts.
How do you see it? The reason I'm asking is that it can affect how we document the feature, what we recommend, etc.
This PR copies the files from
pytest-subtests
and performs minimal integration.I'm opening this to gauge whether everyone is on board with integrating this feature into the core.
Why?
Pros
subtests
is a standardunittest
feature, so it makes sense for pytest to support it as well.Cons
TODO ✅
If everyone is on board, I will take the time this week to polish it and get it ready to merge ASAP:
Related