Why all fixtures in pytest are global?
For example:
tests/
conftest.py
# content of tests/conftest.py
import pytest
#pytest.fixture
def fixture_1():
pass
test_something.py
# content of tests/test_something.py
def test_something(fixture_2):
assert fixture_2 != None
subfolder/
conftest.py
# content of tests/subfolder/conftest.py
import pytest
#pytest.fixture
def fixture_2(username):
pass
As you can see I can get fixture_2 from second level in test from first level folder.
Is any possibility to set fixture just for one package?
Related
I have a conftest.py and a plugin, both defining the same fixture with different implementations:
conftest.py
import pytest
#pytest.fixture
def f():
yield 1
plugin
import pytest
#pytest.fixture
def f():
yield 2
when installing the plugin, the conftest still overrides the plugin, so a test file will only see the conftest fixture, i.e.
test_.py
def test(f):
assert f == 1 # True
I want to be able to do something like this:
If the plugin is not installed, continue
Else, from the conftest plugin, yield the value of the plugin's fixture
I managed to get half of the way:
conftest.py
import pytest
#pytest.fixture
def f(pytestconfig):
if pytestconfig.pluginmanager.has_plugin(plugin_name):
# now what? I have get_plugin and import_plugin, but I'm not able to get the fixture from there...
The easiest way I see is to try and get the plugin fixture value. If the fixture lookup fails, then no plugin defined it and you can do your own thing. Example:
import pytest
from _pytest.fixtures import FixtureLookupError
#pytest.fixture
def f(request):
try: # try finding an already declared fixture with that name
yield request.getfixturevalue('f')
except FixtureLookupError:
# fixture not found, we are the only fixture named 'f'
yield 1
I am having a fixture in conftest.py
#pytest.fixture(scope="function", autouse=True)
#pytest.mark.usefixtures
def pause_on_assert():
yield
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Similarly there are many other fixures in the conftest.py with scope as session, module
My test cases looks like this
test.py
#pytest.fixture(scope="function", autouse=True)
def _wrapper:
print("pre condition")
yield
print("post condition")
def test_abc():
assert 1==0
Problem is I want my fixture that is there in conftest.py to run before yield of fixture that is in my testcase
How can i change order in the way fixture are executed
Here is an example of running my conftest.py function that prints "A" before my test function that prints "B".
cd to the parent directory, for this example it is py_tests and run.
pytest -s -v
The output is:
A
setting up
B
PASSED
With directory structure:
py_tests
-conftest.py
-tests
--tests.py
Files:
conftest.py
import pytest
#pytest.fixture(scope="function")
def print_one():
print("\n")
print("A")
test.py
import pytest
class Testonething:
#pytest.fixture(scope="function", autouse=True)
def setup(self, print_one):
print("setting up")
def test_one_thing(self):
print("B")
assert True
If you want to ensure a piece of code runs after the test function, but before all fixtures teardown, I'd advise to use the pytest_runtest_teardown hook instead. Replace the pause_on_assert fixture in your conftest.py with:
def pytest_runtest_teardown(item, nextitem):
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Since your _wrapper is a function-scoped autouse fixture: it will be instantiated before other fixtures within the same scope. So, hot fix is to define _wrapper without autouse=True and try to call that decorator implicitly like:
def test_abc(_wrapper):
assert 1==0
Autouse source
[Update] If you don't have an ability to change your test suites, I suggest you just wipe all local specific _wrapper and refactor your conftest-specified fixture to call _wrapper, because fixture functions can use other fixtures themselves. Your conftest.py will be look like this:
# conftest.py
#pytest.fixture(scope="function", autouse=True)
def _wrapper(pause_on_assert):
print("pre condition")
yield
print("post condition")
#pytest.fixture()
def pause_on_assert():
yield
if hasattr(sys, 'last_value') and isinstance(sys.last_value, AssertionError):
tkinter.messagebox.showinfo(sys.last_value)
Modularity source
First of all, the relevant portion of my project directory looks like:
└── my_package
├── my_subpackage
│ ├── my_module.py
| └── other_module.py
└── tests
└── my_subpackage
└── unit_test.py
I am writing some tests in unit_test.py that require mocking of an external resource at the module level. I would like to use a pytest fixture with module level scope and pytest monkeypatch to acomplish this. Here is a snippet of what I have tried in unit_test.py:
import unittest.mock as mock
import pytest
from my_package.my_subpackage.my_module import MyClass
#pytest.fixture(scope='function')
def external_access(monkeypatch):
external_access = mock.MagicMock()
external_access.get_something = mock.MagicMock(
return_value='Mock was used.')
monkeypatch.setattr(
'my_package.my_subpackage.my_module.ExternalAccess.get_something',
external_access.get_something)
def test_get_something(external_access):
instance = MyClass()
instance.get_something()
assert instance.data == 'Mock was used.'
Everything works just fine. But when I try to change line 8 from #pytest.fixture(scope='function') to #pytest.fixture(scope='module'), I get the following error.
ScopeMismatch: You tried to access the 'function' scoped fixture 'monkeypatch' with a 'module' scoped request object, involved factories
my_package\tests\unit_test.py:7: def external_access(monkeypatch)
..\..\Anaconda3\envs\py37\lib\site-packages\_pytest\monkeypatch.py:20: def monkeypatch()
Does anyone know how to monkeypatch with module level scope?
In case anyone wants to know, this is what the two modules look like as well.
my_module.py
from my_package.my_subpackage.other_module import ExternalAccess
class MyClass(object):
def __init__(self):
self.external_access = ExternalAccess()
self.data = None
def get_something(self):
self.data = self.external_access.get_something()
other_module.py
class ExternalAccess(object):
def get_something(self):
return 'Call to external resource.'
I found this issue which guided the way. I needed to make a few changes to the solution for module level scope. unit_test.py now looks like this:
import unittest.mock as mock
import pytest
from my_package.my_subpackage.my_module import MyClass
#pytest.fixture(scope='module')
def monkeymodule():
from _pytest.monkeypatch import MonkeyPatch
mpatch = MonkeyPatch()
yield mpatch
mpatch.undo()
#pytest.fixture(scope='module')
def external_access(monkeymodule):
external_access = mock.MagicMock()
external_access.get_something = mock.MagicMock(
return_value='Mock was used.')
monkeymodule.setattr(
'my_package.my_subpackage.my_module.ExternalAccess.get_something',
external_access.get_something)
def test_get_something(external_access):
instance = MyClass()
instance.get_something()
assert instance.data == 'Mock was used.'
This has gotten simpler as of pytest 6.2, thanks to the pytest.MonkeyPatch class and context-manager (https://docs.pytest.org/en/6.2.x/reference.html#pytest.MonkeyPatch). Building off Rich's answer, the monkeymodule fixture can now be written as follows:
#pytest.fixture(scope='module')
def monkeymodule():
with pytest.MonkeyPatch.context() as mp:
yield mp
#pytest.fixture(scope='function')
def external_access(monkeymodule):
external_access = mock.MagicMock()
external_access.get_something = mock.MagicMock(
return_value='Mock was used.')
monkeymodule.setattr(
'my_package.my_subpackage.my_module.ExternalAccess.get_something',
external_access.get_something)
I am new to Python and I have a doubt in pytest
test_client.py
# Simple Example tests
import pytest
def test_one():
assert False == False
def test_two():
assert True == True
def cleanup():
# do some cleanup stuff
conftest.py
import pytest
import test_client
#pytest.fixture(scope="session", autouse=True)
def do_clean_up(request):
request.addfinalizer(test_client.cleanup)
Is it possible to move the fixture defined in conftest.py to the test_client.py thereby eliminating having conftest.py
Yes. Why didn't you simply try? ;)
Fixtures are put in conftest.py files to be able to use them in multiple test files.
I have a bunch of tests written using pytest. There are all under a directory dir. For example:
dir/test_base.py
dir/test_something.py
dir/test_something2.py
...
The simplified version of code in them is as follows:
test_base.py
import pytest
class TestBase:
def setup_module(module):
assert False
def teardown_module(module):
assert False
test_something.py
import pytest
from test_base import TestBase
class TestSomething(TestBase):
def test_dummy():
pass
test_something2.py
import pytest
from test_base import TestBase
class TestSomethingElse(TestBase):
def test_dummy2():
pass
All my test_something*.py files extend the base class in test_base.py. Now I wrote setup_module(module) and teardown_module(module) methods in test_base.py. I was expecting the setup_module to be called once for all tests, and teardown_module() to be called at the end, once all tests are finished.
But the functions don’t seem to be getting called? Do I need some decorators for this to work?
The OP's requirement was for setup and teardown each to execute only once, not one time per module. This can be accomplished with a combination of a conftest.py file, #pytest.fixture(scope="session") and passing the fixture name to each test function.
These are described in the Pytest fixtures documentation
Here's an example:
conftest.py
import pytest
#pytest.fixture(scope="session")
def my_setup(request):
print '\nDoing setup'
def fin():
print ("\nDoing teardown")
request.addfinalizer(fin)
test_something.py
def test_dummy(my_setup):
print '\ntest_dummy'
test_something2.py
def test_dummy2(my_setup):
print '\ntest_dummy2'
def test_dummy3(my_setup):
print '\ntest_dummy3'
The output when you run py.test -s:
collected 3 items
test_something.py
Doing setup
test_dummy
.
test_something2.py
test_dummy2
.
test_dummy3
.
Doing teardown
The name conftest.py matters: you can't give this file a different name and expect Pytest to find it as a source of fixtures.
Setting scope="session" is important. Otherwise setup and teardown will be repeated for each test module.
If you'd prefer not to pass the fixture name my_setup as an argument to each test function, you can place test functions inside a class and apply the pytest.mark.usefixtures decorator to the class.
Put setup_module and teardown_module outside of a class on module level. Then add your class with your tests.
def setup_module(module):
"""..."""
def teardown_module(module):
"""..."""
class TestSomething:
def test_dummy(self):
"""do some tests"""
For more info refer to this article.
setup_module/teardown_module are called for the module where the eventual (derived) tests are defined. This also allows to customize the setup. If you only ever have one setup_module you can put it to test_base.py and import it from the other places. HTH.
First of all it is a good practice to put all tests in a module called "tests":
<product>
...
tests/
__init__.py
test_something.py
Secondly I think you should use setup_class/teardown_class methods in your base class:
import unittest
class MyBase(unittest.TestCase):
#classmethod
def setup_class(cls):
...
#classmethod
def teardown_class(cls):
...
More info: http://pytest.org/latest/xunit_setup.html