I am creating an object within conftest.py and use it in some fixtures. I also need to use this object within my test-modules. Currently I am importing conftest.py inside my test-modules and make use of that 'helper' object. I am pretty sure that this is not the recommended way. I am looking forward to your suggestions.
Thank you :)
Following is the dummy-coded version of my question:
conftest.py
import pytest
class Helper():
def __init__(self, img_path:str):
self.img_path = img_path
def grayscale(self):
pass
def foo(self):
pass
helper = Helper("sample.png")
#pytest.fixture()
def sample():
return helper.grayscale()
test_module.py
import conftest
helper = conftest.helper
def test_method1(sample):
helper.foo()
...
As already commented, I also handled such scenarios by fixtures before if I have a helper class in tests.
conftest.py
import pytest
class Helper():
def __init__(self, img_path: str):
self.img_path = img_path
def grayscale(self):
pass
def foo(self):
pass
#pytest.fixture(scope="session")
def helper():
return Helper("sample.png")
#pytest.fixture()
def sample(helper):
return helper.grayscale()
test_module.py
def test_method1(helper, sample):
helper.foo()
Related
Lets say I have a fixture like this:
#pytest.fixture(scope="class")
def some_fixture(some_paramaters):
do_something
yield
do_something
And i want to use it in this way:
#pytest.mark.usefixtures('some_fixtures')
class TestSomething:
code....
Is it possible to pass the parameter some_paramter using #pytest.mark.usefixtures decorator. Or is there any way to pass that parameter?
You could use something like this by using an autouse fixture.
import pytest
import requests
#pytest.fixture(scope="class")
def some_fixture(some_parameters):
do_something
yield
do_something
class TestSomething:
#pytest.fixture(autouse=True)
def _calling_method(self, some_fixture):
self._response = some_fixture(some_parameters)
class MyTestCase(unittest.Testcase):
def setUp(self):
self.something = True
#pytest.fixture(autouse=True)
def MyTestMethod(self, frozentime):
fn(self.something) # self.something is NOT defined
If I use #pytest.fixture(autouse=True) I end up with some strange behavior from PyTest. Instead of calling my setUp method before the test method, PyTest skips the setUp and calls MyTestMethod as if it was a PyTest MyTestFunction which of course does not work very well.
How do I get MyTestMethod to use the frozentime fixture without ignoring the setUp method that should be called first.
class MyTestCase(unittest.Testcase):
def setUp(self):
self.something = True
##pytest.fixture(autouse=True)
def MyTestMethod(self, frozentime): # Fails on call, because it needs too many arguments.
fn(self.something)
That's because the autouse fixtures are executed before the setUp/tearDown methods:
Note
Due to architectural differences between the two frameworks, setup and teardown for unittest-based tests is performed during the call phase of testing instead of in pytest‘s standard setup and teardown stages. This can be important to understand in some situations, particularly when reasoning about errors. For example, if a unittest-based suite exhibits errors during setup, pytest will report no errors during its setup phase and will instead raise the error during call.
Source
There's nothing you can do to work around this behaviour. You can either move the fixture-relevant code out of setUp/tearDown methods, for example: if self.flag is used in class-scoped fixtures, you can replace
class Tests(unittest.TestCase):
def setUp(self):
self.flag = True
def tearDown(self):
self.flag = False
#pytest.fixture(autouse=True)
def myfixture(self):
print(self.flag)
with
class Tests(unittest.TestCase):
#pytest.fixture(autouse=True)
def prepare_flag(self):
self.flag = True
yield
self.flag = False
#pytest.fixture(autouse=True)
def myfixture(self, prepare_flag):
print(self.flag)
Or you can move all the setUp relevant code from fixtures:
class Tests(unittest.TestCase):
def setUp(self):
self.flag = True
#pytest.fixture(autouse=True)
def myfixture(self, somearg):
fn(self.flag, somearg)
becomes
class Tests(unittest.TestCase):
def setUp(self):
self.flag = True
fn(self.flag, self._somearg)
#pytest.fixture(autouse=True)
def assign_stuff(self, somearg):
self._somearg = somearg
As #hoefling mentioned, the two lifecycles are incompatible... but that can be hacked around if you're not aiming for drop-in compatibility.
import pytest
from pytestqt.plugin import QtBot
from unittest import TestCase
from myproject import custom_widget
#pytest.fixture(scope="class")
def qtbot_adapter(qapp, request):
"""Adapt qtbot fixture for usefixtures and unittest.TestCase"""
request.cls.qtbot = QtBot(request)
def with_updown(function):
"""Wrapper to bodge setUp/tearDown into fixtures+TestCase"""
def test_wrapper(self, *args, **kwargs):
__tracebackhide__ = True
if callable(getattr(self, 'up', None)):
self.up()
try:
function(self, *args, **kwargs)
finally:
if callable(getattr(self, 'down', None)):
self.down()
test_wrapper.__doc__ = function.__doc__
return test_wrapper
#pytest.mark.usefixtures("qtbot_adapter")
class MyTestCase(TestCase):
def up(self):
self.widget = custom_widget.CustomWidget()
self.widget.show()
#with_updown
def test_some_property(self):
with self.qtbot.waitSignal(self.widget.my_signal,
timeout=300):
self.widget.do_thing()
self.assertEqual(self.widget.get_thing(), 'foo')
I have two test case modules TestClassA, TestClassB. On test_1_that_needs_resource_a in TeseClass A, I am using fixture so I can call resource_a and pass parameters before executing testcase. How can I use the same fixture on setup_class in TestClassB so all testcase
in TestClassB so the fixture can be called one time before all testcase in TestClassB
import pytest
#pytest.fixture(scope='function')
def resource(request):
print('resources_a_setup()')
return request.param
class TestClassA:
#classmethod
def setup_class(cls):
print('\nsetup_class()')
#classmethod
def teardown_class(cls):
print('\nteardown_class()')
#pytest.mark.parametrize('resource', [dict(), dict(name=1)], indirect=['resource'])
def test_1_that_needs_resource_a(self, resource):
assert resource == dict()
print(f'\ntest_1_that_needs_resource_a(), {resource}')
class TestClassB:
## I would like to call resource fixture with passing some parameter on setup_class here
#classmethod
def setup_class(cls):
print('\nsetup_class()')
#classmethod
def teardown_class(cls):
print('\nteardown_class()')
def test_1_that_needs_resource_a(self):
print('\ntest_1_that_needs_resource_a()')
With python and nosetests I have the following setup:
- package
- __init__.py
- test1.py
- test2.py
The __init__.py module contains a set up function
def setup():
print("Setup called")
var = 42
which will be used later to create a unique identified (different between running the tests, but the same for all the tests inside the package).
How can the tests itself access this variable (in this example case var)? The test scripts are just some stubs:
from nose.tools import assert_true
class TestSuite(object):
def test1(self):
# How to get content of 'var' here?
assert_true(True)
Is there some pythonic way to do this, or just use an environment variable to do this?
nose calls .setup() methods inside classes:
class Test:
def setup(self):
self.var = 1
def test_print_var(self):
print(self.var)
This also applies to methods inherited from elsewhere:
class TestBase:
def setup(self):
self.var = 1
class Test(TestBase):
def test_print_var(self):
print(self.var)
I use py.test for testing.
In setup_class() I need to use tmpdir for my class constructor:
class TestMyClass:
def setup_class(self):
self.t = MyClass(path=tmpdir)
def test_test(self):
assert True
And I have an error:
NameError: name 'tmpdir' is not defined
I can't use setup_class(self, tmpdir).
If I use this code:
def test_needsfiles(tmpdir):
print(tmpdir)
assert 0
It's work, but I need tmpdir in my class constructor.
How to do this?
Thanks!
UPD
I try to do this:
#pytest.yield_fixture()
def constructor(tmpdir):
_t = MyClass(path=str(tmpdir))
yield _t
class TestMyClass:
def test_test(self, constructor):
pass
But I can't use scopes in fixture:
ScopeMismatch: You tried to access the 'function' scoped fixture 'tmpdir' with a 'module' scoped request object, involved factories
I do this:
class TestMyClass:
#pytest.fixture(autouse=True)
def setup(self, tmpdir):
self.tmpdir = tmpdir.strpath
If you don't want to use #pytest.fixture(autouse=True) as #santon suggested, but you want to create a fixture outside TestMyClass (like you write in the UPD part), you can try this:
#pytest.fixture
def t(tmpdir):
return MyClass(tmpdir)
class TestMyClass:
def test_test(self, t):
assert True
If you don't want to return anything in a fixture, but for example go to a temp directory, you can also do:
#pytest.fixture
def t(tmpdir):
os.chdir(str(tmpdir))
#pytest.mark.usefixtures("t")
class TestMyClass:
def test_test(self):
assert True
You can use tempfile module to handle with temporary files and dirs. In setup you can create temp dir with mkdtemp and delete it at tearDown from test class.
import shutil, tempfile
import unittest
class TestMyClass(unittest.TestCase):
def setUp(self):
self.tmp_dir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tmp_dir)