tmpdir in py.test setup_class - python

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)

Related

How to pass a parameter to a class scope fixture

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)

Is it discouraged to import conftest.py within test-modules?

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()

How to use pytest fixtures with Unittest methods

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')

using the pytest.fixture scope function on setup_class

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()')

Reloading the current module when unittesting

When running a simple unittest it would sometimes be easier to be able to keep the tests inside the class. However, I don't know how to reload the current module, and so whenever that's needed I have to move the tests into a separate module. Is there a way around this?
module: foo
import unittest
class MyObject
...
class MockMyObject
...
class TestMock(unittest.TestCase):
def setUp(self):
MyObject = MockMyObject
mocked = MyObject()
def tearDown(self):
reload(foo) # what goes here?
def testFunction(self):
mocked.do_mocked_function()
if __name__ == "__main__":
unittest.main()
The way I've found to handle this is to import sys and reload(sys.modules[__name__]) in the tearDown method, but I'm wondering if there is a better method.
You can save your original class in a variable and restore it in the tearDown function.
Here is an example:
class TestMock(unittest.TestCase):
original = MyObject
def setUp(self):
global MyObject
MyObject = MockMyObject
def tearDown(self):
global MyObject
MyObject = TestMock.original
def testFunction(self):
MyObject().do_mocked_function()
that's not a good idea to reload your module.
class TestMock(unittest.TestCase):
def setUp(self):
MyObject = MockMyObject
self.mocked = MyObject()
def tearDown(self):
pass
def testFunction(self):
self.mocked.do_mocked_function()

Categories

Resources