-
Notifications
You must be signed in to change notification settings - Fork 3
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
PyTest FixtureRequest #74
Comments
I think this is better raised as a feature request at pylint-dev/pylint-pytest |
Hello @dylan-at-na! Are you sure this is working like you are demonstrating? We do actually have a test like that in the repo: https://github.com/pylint-dev/pylint-pytest/blob/8310a88916e4b0b7462fc525c56d60b9af4dc126/tests/input/no-member/not_using_cls.py I have tried to replicate your setup $ git --no-pager diff
diff --git a/tests/input/no-member/not_using_cls.py b/tests/input/no-member/not_using_cls.py
index 95438af..16caf33 100644
--- a/tests/input/no-member/not_using_cls.py
+++ b/tests/input/no-member/not_using_cls.py
@@ -7,6 +7,14 @@ class TestClass:
def setup_class(request):
clls = request.cls
clls.defined_in_setup_class = 123
+ print(clls.defined_in_setup_class)
def test_foo(self):
assert self.defined_in_setup_class
+
+ @pytest.fixture(scope="class")
+ def setup_teardown_environment(self, request: pytest.FixtureRequest):
+ # The following line assigns a value to `temp1`
+ # which can be accessed throughout the test class via `self`.
+ request.cls.temp1 = 54321
+ print(self.temp1)
$ pytest tests/input/no-member/not_using_cls.py -s
======================================= test session starts =======================================
platform linux -- Python 3.11.9, pytest-7.4.3, pluggy-1.3.0 -- ~/pylint-pytest/.venv/bin/python3.11
cachedir: .pytest_cache
rootdir: ~/pylint-pytest
configfile: pyproject.toml
plugins: cov-5.0.0
collected 1 item
tests/input/no-member/not_using_cls.py::TestClass::test_foo 123
PASSED
======================================== 1 passed in 0.01s ======================================== but, as you can see, I am unable to make your The test is printing out You MUST mark the |
(Let's find out if the reporter is using pylint-pytest or not.) |
@stdedos Please see the below example which demonstrates a functional example (autouse is ignored in the first example, but is used further down)... import pytest
class TestExample:
@pytest.fixture(scope="class")
def setup_teardown_username(self, request: pytest.FixtureRequest):
request.cls.username = 'dylan'
yield True
del request.cls.username
@pytest.fixture(scope="function")
def setup_teardown_password(self, request: pytest.FixtureRequest, setup_teardown_username):
request.cls.password = '*****'
yield setup_teardown_username, True
del request.cls.password
def test_username_and_password(self, setup_teardown_password):
assert self.username == 'dylan'
assert self.password == '*****' And the log output from running the above...
The following screenshot demonstrates the error being thrown... @jacobtylerwalls The following is the result of running pylint against the above code with pylint-pytest plugin enabled. Relevant lines are marked with ***
If I now add import pytest
class TestExample:
@pytest.fixture(scope="class", autouse=True)
def setup_teardown_username(self, request: pytest.FixtureRequest):
request.cls.username = 'dylan'
yield True
del request.cls.username
@pytest.fixture(scope="function", autouse=True)
def setup_teardown_password(self, request: pytest.FixtureRequest):
request.cls.password = '*****'
yield True
del request.cls.password
def test_username_and_password(self):
assert self.username == 'dylan'
assert self.password == '*****' Which again, passes.
However, running PyLint on this produces the same errors (relevant lines marked with ***)...
Interestingly enough if I use your code sample located at https://github.com/pylint-dev/pylint-pytest/blob/8310a88916e4b0b7462fc525c56d60b9af4dc126/tests/input/no-member/not_using_cls.py I don't get the error
This prompted me to attempt PyLint with In a last ditch effort I assigned import pytest
class TestExample:
@pytest.fixture(scope="class", autouse=True)
def setup_teardown_username(self, request: pytest.FixtureRequest):
clls = request.cls
clls.username = 'dylan'
yield True
del request.cls.username
@pytest.fixture(scope="function", autouse=True)
def setup_teardown_password(self, request: pytest.FixtureRequest):
clls = request.cls
clls.password = '*****'
yield True
del request.cls.password
def test_username_and_password(self):
assert self.username == 'dylan'
assert self.password == '*****' Again, this passes. And it removes the error line for the
(Incorrect assumption, see follow up comment for clarification) |
Quick follow up - it isn't import pytest
class TestExample:
@pytest.fixture(scope="class", autouse=True)
def setup_teardown_username(self, request: pytest.FixtureRequest):
mocha = request.cls
mocha.username = 'dylan'
yield True
del request.cls.username
@pytest.fixture(scope="function", autouse=True)
def setup_teardown_password(self, request: pytest.FixtureRequest):
clls = request.cls
clls.password = '*****'
yield True
del request.cls.password
def test_username_and_password(self):
assert self.username == 'dylan'
assert self.password == '*****' Passes. PyLint:
Again this does not resolve the issue in the |
FWIW: Your search result gives exactly one result for me
... but for now, it MUST be like that. I am not sure why the original author chose to write this test (
Exactly 🙃
I will try this locally too. However, as of now, for a pylint-pytest/pylint_pytest/utils.py Lines 83 to 107 in 8310a88
I will try all scopes and see what their result is. And then code/test appropriately. ... it seems that this is getting quick traction, so I'll push a branch within the day. One thing that I haven't been able to figure out, unfortunately, is that the pylint/pylint-pytest is sensitive to ordering. tl;dr: class TestClass:
@staticmethod
@pytest.fixture(scope="class", autouse=True)
def setup_class(request):
clls = request.cls
clls.defined_in_setup_class = 123
def test_foo(self):
assert self.defined_in_setup_class works, class TestClass:
def test_foo(self):
assert self.defined_in_setup_class
@staticmethod
@pytest.fixture(scope="class", autouse=True)
def setup_class(request):
clls = request.cls
clls.defined_in_setup_class = 123 fails.
|
For anybody else who comes across this issue - the easiest temporary workaround with minimal changes to your code is to introduce a fixture which is import pytest
class TestExample:
@pytest.fixture(scope='class', autouse=True)
def setup_teardown_request_cls(self, request: pytest.FixtureRequest):
_cls = request.cls
_cls.username = None
_cls.password = None
@pytest.fixture(scope="class")
def setup_teardown_username(self, request: pytest.FixtureRequest):
request.cls.username = 'dylan'
yield True
del request.cls.username
@pytest.fixture(scope="function")
def setup_teardown_password(self, request: pytest.FixtureRequest, setup_teardown_username: bool):
request.cls.password = '*****'
yield True
del request.cls.password
def test_username_and_password(self, setup_teardown_password: bool):
assert self.username == 'dylan'
assert self.password == '*****' This has the added benefit of isolating the initialization, thus being easy to remove when and if @stdedos changes are in place. You can of course initialize your variables to any default value you like but for demonstrations sake I have used |
It seems we already support the (weird) scenario that someone does `clls = request.cls`, and subsequently uses `clls.X = Y` However, it appears that we are missing the trivial `request.cls.X = Y` case References #74 Additionally: * Factorize condititons for semantic naming * `pylint_pytest/utils.py#_is_class_autouse_fixture`: Class-defined `@pytest.fixture(autouse=True)` need not explicitly define `scope="class"` We set `is_class` via `isinstance(function.parent, astroid.ClassDef)`, and un-set it if explicitly defined as "not `"class"`" * The test currently is not passing, so we surgically mark it as `xfail` (for details, see the code) Signed-off-by: Stavros Ntentos <[email protected]> Small improvements following the `no-member` improvements We can "never know" what `node.targets` is. Add a type guard and a `logging.warning` for non-matches. Which leads to creating the `node_line_as_string_w_highlight`, to be able to have a more direct visual feedback of "what a `node` is". We are also creating a `no_error()`, `noerr_or_default()` family of functions, since using functions as complex as `node_line_as_string_w_highlight` could lead to errors that are really not desired in the context of logging. Additionally, make the `pprint(self.msgs)` testing function conditional on very verbose - and expand it with `node_line_as_string_w_highlight`. Finally, make some "not-docstrings" comments, and fix some grammar issues. Signed-off-by: Stavros Ntentos <[email protected]> `tests/base_tester.py`: Warn on using the short Message ID for instantiation With `PylintPytestWarning`, `PylintMessageDefinition`, give (me) a notice when creating new not-compliant `BasePytestTester` sub-classes Signed-off-by: Stavros Ntentos <[email protected]> v2: `no-member`: Support tracking/ignoring `request.cls.X` / `self.X` v3: `no-member`: Support fine-grained attribute declaration In order to "properly" analyze fixtures that might change the class itself (which it can usually be out of order typed), we need to walk the `fixture` `FunctionDef`s in advance, in their topological order, per-scope For that, we need to hand-roll out own ASTWalker / ASTVisitor (pylint's is a bit tight to their `checker` implementation) This enables us to (almost) correctly allow pylint to introspect `used-before-assignment` issues (see `tests/input/no-member/not_using_cls.py` changes) Signed-off-by: Stavros Ntentos <[email protected]>
It seems we already support the (weird) scenario that someone does `clls = request.cls`, and subsequently uses `clls.X = Y` However, it appears that we are missing the trivial `request.cls.X = Y` case Fixes #74 Additionally: * Factorize condititons for semantic naming * Make some "not-docstrings" comments, and fix some grammar issues. In order to "properly" analyze fixtures that might change the class itself (which it can usually be out of order typed), we need to walk the `fixture` `FunctionDef`s in advance, in their topological order, per-scope. For that, we need to hand-roll out own ASTWalker / ASTVisitor (pylint's is a bit tight to their `checker` implementation). This enables us to (almost) correctly allow pylint to introspect `used-before-assignment` issues (see `tests/input/no-member/not_using_cls.py` changes) For project internals: * Split `pylint_pytest/utils.py` to modules Signed-off-by: Stavros Ntentos <[email protected]>
It seems we already support the (weird) scenario that someone does `clls = request.cls`, and subsequently uses `clls.X = Y` However, it appears that we are missing the trivial `request.cls.X = Y` case Fixes #74 Additionally: * Factorize condititons for semantic naming * Make some "not-docstrings" comments, and fix some grammar issues. In order to "properly" analyze fixtures that might change the class itself (which it can usually be out of order typed), we need to walk the `fixture` `FunctionDef`s in advance, in their topological order, per-scope. For that, we need to hand-roll out own ASTWalker / ASTVisitor (pylint's is a bit tight to their `checker` implementation). This enables us to (almost) correctly allow pylint to introspect `used-before-assignment` issues (see `tests/input/no-member/not_using_cls.py` changes) For project internals: * Split `pylint_pytest/utils.py` to modules Signed-off-by: Stavros Ntentos <[email protected]>
It seems we already support the (weird) scenario that someone does `clls = request.cls`, and subsequently uses `clls.X = Y` However, it appears that we are missing the trivial `request.cls.X = Y` case Fixes #74 Additionally: * Factorize condititons for semantic naming * Make some "not-docstrings" comments, and fix some grammar issues. In order to "properly" analyze fixtures that might change the class itself (which it can usually be out of order typed), we need to walk the `fixture` `FunctionDef`s in advance, in their topological order, per-scope. For that, we need to hand-roll out own ASTWalker / ASTVisitor (pylint's is a bit tight to their `checker` implementation). This enables us to (almost) correctly allow pylint to introspect `used-before-assignment` issues (see `tests/input/no-member/not_using_cls.py` changes) For project internals: * Split `pylint_pytest/utils.py` to modules Signed-off-by: Stavros Ntentos <[email protected]>
It seems we already support the (weird) scenario that someone does `clls = request.cls`, and subsequently uses `clls.X = Y` However, it appears that we are missing the trivial `request.cls.X = Y` case Fixes #74 Additionally: * Factorize condititons for semantic naming * Make some "not-docstrings" comments, and fix some grammar issues. In order to "properly" analyze fixtures that might change the class itself (which it can usually be out of order typed), we need to walk the `fixture` `FunctionDef`s in advance, in their topological order, per-scope. For that, we need to hand-roll out own ASTWalker / ASTVisitor (pylint's is a bit tight to their `checker` implementation). This enables us to (almost) correctly allow pylint to introspect `used-before-assignment` issues (see `tests/input/no-member/not_using_cls.py` changes) For project internals: * Split `pylint_pytest/utils.py` to modules Signed-off-by: Stavros Ntentos <[email protected]>
Bug description
Command used
pylint ${file}
Pylint output
Expected behavior
PyLint should not identify this as a problem at all, as request.cls and self are intrinsically linked when operating within the same test class.
Pylint version
OS / Environment
Ubuntu 20
The text was updated successfully, but these errors were encountered: