How to forbid pytest recreate instance of class when I use @pytest.mark.incremental? I want to set value in self but pytest recreate instance and in next method I get exception AttributeError.
If there is no such possibility then consider this issue as a feature issue.
can you provide some sample code?
pytest currently won't collect tests with __init__:
class Test2:
def __init__(self, *args, **kwargs):
pass
def test(self): pass
=============================== warnings summary ===============================
t.py:13
/tmp/t/t.py:13: PytestWarning: cannot collect test class 'Test2' because it has a __init__ constructor
class Test2:
-- Docs: https://docs.pytest.org/en/latest/warnings.html
There is one way to define __init__ it seems, by extending unittest.TestCase:
class Test(TestCase):
def __init__(self, *args, **kwargs):
print('init!')
super().__init__(*args, **kwargs)
def test(self): pass
def test1(self): pass
def test2(self): pass
but both unittest and pytest both run the __init__ every time:
$ python -m unittest t
init!
init!
init!
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
$ pytest t.py -s
============================= test session starts ==============================
platform linux -- Python 3.6.6, pytest-4.0.1, py-1.7.0, pluggy-0.8.0
rootdir: /tmp/t, inifile:
collected 3 items
t.py init!
.init!
.init!
.
=========================== 3 passed in 0.01 seconds ===========================
and I don't think it would be wise to differ in this case :)
sharing mutable state between tests is a slippery slope, but if you must I'd suggest using a class scoped fixture:
import pytest
class Test:
@pytest.fixture(autouse=True, scope='class')
def state(self):
return {}
def test(self, state):
state['foo'] = 'bar'
def test1(self, state):
assert state['foo'] == 'bar'
Note that this is just an example of what you _could_ do, I'd strongly suggest refactoring your tests so they don't depend on ordering / pollution.
I want test creation selection, modifiaction and deletion user in my app. In first test I create user and function return of creation user return me user id. I store this id in instanse of test class using self.user_id=user_id and I want to get user_id in next tests but pytest recreate instance and user_id not in new instance. I propose add copy state of instance for incremental mark.
I suggest using fixtures. Where are you getting an incremental mark from? I'm not familiar with that being something that pytest provides?
@pytest.mark.incremental
@heckad pytest itself does not interpret that mark
@heckad pytest allows arbitrary marks to be defined. A few have special meaning such as parametrize and xfail, others can be defined by plugins or contest.py files.
What the reason for re-creationinstance for each test?
consistency, both with the way that unittest does this and to make tests as repeatable as possible (you can run just a single test and so it is the same experience whether you run an individual test or a collection of tests from the test's point of view). test classes really shouldn't be a container for state but just a way to organize tests, I'd suggest using fixtures (which are designed for this) instead.
Is it even possible to add mark wich say copy state of self? Where can I read about creating marks and their capabilities?
It seems to me logical that tests inside the class should be performed within some context that they themselves will change. Why is it necessary to start something else if you already have self?
@heckad Tests should not save state from one test to another as a general rule, most users will shoot themselves in the foot if they do and probably create a messy test suite. For this reason pytest (and other test frameworks) always provide a fresh self for each test. "Copying self" between tests is not really something that is possible today.
We already have provided you with a possible solution (using a class fixture as @asottile suggested), another would save your state in the class instead of self (Test.user_id=user_id). But again, this has the potential to make your tests confusing and hard to run as they depend on specific ordering.
Can you tell where does @pytest.mark.incremental comes from? pip list might provide a clue.
Here's the docs on this and how I got to them.
is it possible to do what you want? probably? maybe? is it a good idea -- no I don't think so.
If it not good idea why then do we need classes? And how to make a chain of tests with context var without classes?
you don't need classes, that's one of the main selling points of pytest is it enables and encourages you to write function-based tests instead of class-based tests. pytest still supports class-based tests to allow you to migrate from older code bases and because sometimes using a class for grouping related tests is useful.
you can use fixtures as a way to share state as I demonstrated above -- it doesn't have to be a class fixture even, you could use scope='module' or scope='session'. But usually sharing state between tests or requiring an ordering for tests indicates a design problem with the tests themselves (which generally should be free-standing and side-effect free)
This issue also popped up in my tests when i wanted to switch from unittest to pytest testing. It should at least be mentioned in the docs about running unittests, since some unittests won't run anymore with AttributeErrors. There are perfectly good reasons to share state between tests, like when you want to run a specific sequence of tests and keep the steps organized to easily see (auto document) when something goes wrong (e.g. when using a state machine). Ofcourse there are workarounds, but for me the choice is to do extra work or go back to unittest.
After a second look, a lot of extra work: replacing every access to self to something else and/or change the signature of every test method ...
Since self is passed to every test method in a unittest, you would expect it to be useful (be able to retain some state).
Hi @gemerden,
Not sure what you mean, unittest also creates a new instance of each test class for every test execution:
import unittest
class T(unittest.TestCase):
def test_1(self):
self.foo = 'foo'
def test_2(self):
self.assertEqual(self.foo, 'foo')
if __name__ == '__main__':
unittest.main()
位 python .tmp\test_ut.py
.E
======================================================================
ERROR: test_2 (__main__.T)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".tmp\test_ut.py", line 9, in test_2
self.assertEqual(self.foo, 'foo')
AttributeError: 'T' object has no attribute 'foo'
----------------------------------------------------------------------
Ran 2 tests in 0.001s
Just double checked, you are right, i remembered wrong, too bad, sorry to bother you, will have to do the work anyway.
I see, thanks for the follow up.
There are a few suggestions on how to share state between tests in this thread (although we don't recommend to do so), feel free to ask if anything is not clear.
Most helpful comment
you don't need classes, that's one of the main selling points of pytest is it enables and encourages you to write function-based tests instead of class-based tests. pytest still supports class-based tests to allow you to migrate from older code bases and because sometimes using a class for grouping related tests is useful.
you can use fixtures as a way to share state as I demonstrated above -- it doesn't have to be a class fixture even, you could use
scope='module'orscope='session'. But usually sharing state between tests or requiring an ordering for tests indicates a design problem with the tests themselves (which generally should be free-standing and side-effect free)