My goal is to create a fixture that will run once at the beginning of the class function test and initialize the attributes I need in self. To do this, I created a fixture with the scope of the class and applied it directly to the class. To solve the compatibility problem of pytest with asynchronous code, pytest-asyncio was used.
My minimally reproducible example:
import pytest
#pytest.fixture(scope="class")
async def setup_teardown(request):
request.cls.test_number = '123'
yield
# ...
#pytest.mark.asyncio
#pytest.mark.usefixtures("setup_teardown")
class Test_BaseFunctional():
async def test_my_number(self):
assert self.test_number == '123'
But i'm receiving:
ScopeMismatch: You tried to access the function scoped fixture event_loop with a class scoped request object, involved factories:
tests/issue.py:4: def setup_teardown(request)
I tried many ways, from time to time I got a large-scale non-working code, and in the end I returned to this minimal example in the hope of help from you, dear friends.
The reason was in pytest-asyncio fixtures realization. They has scope "function" which not implicated with higher-level fixtures, such as the scope "class". In this case, we just need to redefine event_loop fixture to eliminate ScopeMismatch.
#pytest.fixture(scope="class")
def event_loop():
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
yield loop
loop.close()
You can put this code into coftest.py file.
Related
So, I have fixtures defined in conftest.py file with scope="class" as I want to run them before each test class is invoked. The conftest file is placed inside project root directory for it to be visible to every test module.
Now in one of the test modules, I have another setup function which I want to run once for that module only. But the problem is setup_class() method is called before running fixtures defined in conftest.py. Is this expected? I wanted it to be opposite because I want to use something done in the fixtures defined in conftest. How to do that?
Code -
conftest.py:
#pytest.fixture(scope="class")
def fixture1(request):
#set a
#pytest.fixture(scope="class")
def fixture1(request):
test_1.py:
#pytest.mark.usefixtures("fixture_1", "fixture_2")
class Test1():
#need this to run AFTER the fixture_1 & fixture_2
def setup_class():
#setup
#get a set in fixture_1
def test_1()
.....
I know that I could simply define a fixture in the test file instead of setup_class but then I will have to specify it in arguments of every test method in order it to be invoked by pytest. But suggestions are welcome!
I have exactly the same problem. Only now I have realized that the problem might be taht the setup_class is called before the fixture >-/
I think that this question is similar to this one
Pytest - How to pass an argument to setup_class?
And the problem is mixing the unittest and pytest methods.
I kind of did what they suggested - I ommitted the setup_class and created a new fixture within the particular test file,
calling the fixture in the conftest.py.
It works so far.
M.
The problem is that you can use the result of a fixture only in test function (or method) which is run by pytest. Here I can suggest a workaround. But of course I'm not sure if it suites your needs.
The workaround is to call the function from a test method:
conftest.py
#pytest.fixture(scope='class')
def fixture1():
yield 'MYTEXT'
test_1.py
class Test1:
def setup_class(self, some_var):
print(some_var)
def test_one(self, fixture1):
self.setup_class(fixture1)
Fixtures and setup_class are two different paradigms to initialize test functions (and classes). In this case, mixing the two creates a problem: The class-scoped fixtures run when the individual test functions (methods) run. On the other hand, setup_class runs before they do. Hence, it is not possible to access a fixture value (or fixture-modified state) from setup_class.
One of the solutions is to stop using setup_class entirely and stick with a fixtures-only solution which is the preferred way in pytest nowadays (see the note at the beginning).
# conftest.py or the test file:
#pytest.fixture(scope="class")
def fixture_1(request):
print('fixture_1')
# the test file:
class Test1():
#pytest.fixture(scope="class", autouse=True)
def setup(self, fixture_1, request):
print('Test1.setup')
def test_a(self):
print('Test1.test_a')
def test_b(self):
print('Test1.test_b')
Note that the setup fixture depends on fixture_1 and hence can access it.
Basically, I'm trying to do a test for each iteration of a list of routes to check that the web pages associated with a particular function return a valid status code.
I want something along the lines of this:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.yield_fixture
def app():
# App context and specific overrides for test env
yield get_app()
#pytest.yield_fixture
def client(app):
yield app.test_client()
#pytest.yield_fixture
def routes(app):
routes = [
'foo',
'bar',
# There's quite a lot of function names here
]
with app.app_context():
for n, route in enumerate(routes):
routes[n] = url_for(route)
# yield url_for(route) #NOTE: This would be ideal, but not allowed.
# convert the routes from func names to actual routes
yield routes
#pytest.mark.parametrize('route', routes)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
I read up that you can't mix parametrize with a fixture as an argument due to something along the lines of interpretation/load/execution order, although, how is this solved in terms of 'best practice'?
I saw a solution where you can generate tests from a function directly, and that seems extremely flexible and might be along the lines of what I want Passing pytest fixture in parametrize (Although I can't use call a fixture decorated function directly, so probably not)
Although, I'm new to pytest and I'd love to see more examples of how to generate tests or perform multiple tests in an iteration with little-to-no restrictions while adhering to proper pytest styling and the DRY principle. (I know about conftest.py)
I'd prioritize versatility/practicality over proper styling if that matters. (within reason, maintainability is a high priority too)
I want to be able to reference the solution to this problem to help guide how I tackle structuring my tests in the future, but I seem to keep hitting roadblocks/limitations or being told by pytest I can't do X solution the way I would expect/want too.
Relevant Posts:
DRY: pytest: parameterize fixtures in a DRY way
Generate tests from a function: Passing pytest fixture in parametrize
Very simply solution (doesn't apply to this case): Parametrize pytest fixture
Flask app context in PyTest: Testing code that requires a Flask app or request context
Avoiding edge-cases with multiple list fixtures: Why does Pytest perform a nested loop over fixture parameters
Pytest fixtures themselves can be parameterized, though not with pytest.mark.parametrize. (It looks like this type of question was also answered here.) So:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.fixture
def app():
app = get_app()
# app context stuff trimmed out here
return app
#pytest.fixture
def client(app):
client = app.test_client()
return client
#pytest.fixture(params=[
'foo',
'bar'
])
def route(request, app):
'''GET method urls that we want to perform mass testing on'''
with app.app_context():
return url_for(request.param)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
The documentation explains it this way:
Fixture functions can be parametrized in which case they will be called multiple times, each time executing the set of dependent tests, i. e. the tests that depend on this fixture. Test functions usually do not need to be aware of their re-running. Fixture parametrization helps to write exhaustive functional tests for components which themselves can be configured in multiple ways.
Extending the previous example, we can flag the fixture to create two smtp_connection fixture instances which will cause all tests using the fixture to run twice. The fixture function gets access to each parameter through the special request object:
# content of conftest.py
import pytest import smtplib
#pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
def smtp_connection(request):
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
yield smtp_connection
print("finalizing {}".format(smtp_connection))
smtp_connection.close()
The main change is the declaration of params with #pytest.fixture, a list of values for each of which the fixture function will execute and can access a value via request.param. No test function code needs to change.
My current solution that I fumbled my way across is this:
import pytest
from flask import url_for
from myflaskapp import get_app
#pytest.fixture
def app():
app = get_app()
# app context stuff trimmed out here
return app
#pytest.fixture
def client(app):
client = app.test_client()
return client
def routes(app):
'''GET method urls that we want to perform mass testing on'''
routes = ['foo', 'bar']
with app.app_context():
for n, route in enumerate(routes):
routes[n] = url_for(route)
return routes
#pytest.mark.parametrize('route', routes(get_app()))
#NOTE: It'd be really nice if I could use routes as a
# fixture and pytest would handle this for me. I feel like I'm
# breaking the rules here doing it this way. (But I don't think I actually am)
def test_page_load(client, route):
assert client.get(route.endpoint).status_code == 200
My biggest issue with this solution is that, I can't call the fixture directly as a function, and this solution requires either that, or doing all the work my fixture does outside of the fixture, which is not ideal. I want to be able to reference this solution to tackle how I structure my tests in the future.
FOR ANYONE LOOKING TO COPY MY SOLUTION FOR FLASK SPECIFICALLY:
My current solution might be worse for some people than it is for me, I use a singleton structure for my get_app() so it should be fine if get_app() is called many times in my case, because it will call create_app() and store the app itself as a global variable if the global variable isn't already defined, basically emulating the behavior of only calling create_app() once.
I need to create a module-scoped fixture where I mock module_a and module_c used inside module_b.Module_B_Class(). I cannot use mock.patch annotation because it provides a function-scoped mock and I also need to assert that, when invoking Module_B_Class a specific function is invoked on module_a and another function is invoked on module_c. So I used pytest-cases unpack_into feature and wrote the following fixture:
#pytest_cases.fixture_plus(scope="module", unpack_into="mocked_module_a,mocked_module_b")
def my_fixture():
with mock.patch('my_top_module.my_sub_module.module_b.module_a') as module_a_mock:
with mock.patch('my_top_module.my_sub_module.module_b.module_c') as module_c_mock:
module_a_mock.my_func = MagicMock(return_value='Hello world')
module_c_mock.my_func_2 = MagicMock(return_value='Good morning')
However, when I run the following:
def test_my_class(mocked_module_a, mocked_module_b):
my_class = Module_B_Class()
my_class.run()
mocked_module_a.assert_called_once()
mocked_module_b.assert_called_once()
which is defined like so
from my_top_module.my_sub_module import module_a
class Module_B_Class():
def run(self):
module_a.my_func()
module_c.my_func2()
the function which is invoked is the original one and not the replaced one. Is the target I am patching the wrong one?
Fixtures are described in the pytest documentation. The basic principle is that the code before the yield is executed before the test (or depending on the scope, before the first test, each module, or each test class), and the code after the yield is executed after the test (or the last test, the module, or class):
#pytest.fixture
def my_fixture():
do_setup()
yield
do_teardown()
You can also return a value using yield, of course.
For a context manager that means, you have to yield before going out of scope:
#pytest.fixture(scope="module")
def my_fixture():
with mock.patch('my_top_module.my_sub_module.module_b.module_a') as module_a_mock:
module_a_mock.my_func = MagicMock(return_value='Hello world')
yield module_a_mock
You can now access the mock via the fixture name in your test, if you need to. In this case, the code returns after the yield only after the tests in the current module are executed, so at that point the patch is reverted.
If you don't do the yield in this case, you get out of scope immediately on fixture execution, meaning that the patch is reverted before you get to the test.
UPDATE:
Here is the version for the updated question which uses pytest_cases:
#pytest_cases.fixture_plus(scope="module",
unpack_into="mocked_module_a,mocked_module_c")
def my_fixture():
with mock.patch(
'my_top_module.my_sub_module.module_b.module_a') as module_a_mock:
with mock.patch(
'my_top_module.my_sub_module.module_b.module_c') as module_c_mock:
module_a_mock.my_func = mock.MagicMock(return_value='Hello world')
module_c_mock.my_func2 = mock.MagicMock(return_value='Good morning')
yield (module_a_mock, module_c_mock)
def test_my_class(mocked_module_a, mocked_module_c):
my_class = Module_B_Class()
my_class.run()
mocked_module_a.my_func.assert_called_once()
mocked_module_c.my_func2.assert_called_once()
Note: I have renamed mocked_module_b to mocked_module_c to avoid confusion. Also assert_called_once had been called on the module instead of the function.
I'm trying to create a test environment with Pytest. The idea is to group test methods into classes.
For every class/group, I want to attach a config fixture that is going to be parametrized. So that I can run all the tests with "configuration A" and then all tests with "configuration B" and so on.
But also, I want a reset fixture, that can be executed before specific methods or all methods of a class.
The problem I have there is, once I apply my reset fixture (to a method or to a whole class), the config fixture seems to work in the function scope instead of the class scope. So, once I apply the reset fixture, the config fixture is called before/after every method in the class.
The following piece of code reproduces the problem:
import pytest
from pytest import *
#fixture(scope='class')
def config(request):
print("\nconfiguring with %s" % request.param)
yield
print("\ncleaning up config")
#fixture(scope='function')
def reset():
print("\nreseting")
#mark.parametrize("config", ["config-A", "config-B"], indirect=True)
##mark.usefixtures("reset")
class TestMoreStuff(object):
def test_a(self, config):
pass
def test_b(self, config):
pass
def test_c(self, config):
pass
The test shows how the config fixture should work, being executed only once for the whole class. If you uncomment the usefixtures decoration, you can notice that the config fixture will be executed in every test method. Is it possible to use the reset fixture without triggering this behaviour?
As I mentioned in a comment, that seems to be a bug in Pytest 3.2.5.
There's a workaround, which is to "force" the scope of a parametrization. So, in this case if you include the scope="class" in the parametrize decorator, you get the desired behaviour.
import pytest
from pytest import *
#fixture(scope='class')
def config(request):
print("\nconfiguring with %s" % request.param)
yield
print("\ncleaning up config")
#fixture(scope='function')
def reset():
print("\nreseting")
#mark.parametrize("config", ["config-A", "config-B"], indirect=True, scope="class")
#mark.usefixtures("reset")
class TestMoreStuff(object):
def test_a(self, config):
pass
def test_b(self, config):
pass
def test_c(self, config):
pass
It depends on which version of pytest you are using.
There are some semantical problems to implement this in older versions of pytest. So, this idea is not yet implemented in older pytest. Someone has already given suggestion to implement the same. you can refer this
"Fixture scope doesn't work when parametrized tests use parametrized fixtures".
This was the bug.
You can refer this
This issue has been resolved in latest version of pytest. Here's the commit for the same with pytest 3.2.5
Hope it would help you.
I have the following scripts:
conftest.py:
import pytest
#pytest.fixture(scope="session")
def setup_env(request):
# run some setup
return("result")
test.py:
import pytest
#pytest.mark.usefixtures("setup_env")
class TestDirectoryInit(object):
def setup(cls):
print("this is setup")
ret=setup_env()
print(ret)
def test1():
print("test1")
def teardown(cls):
print("this teardown")
I get the error:
def setup(cls):
print("this is setup")
> ret=setup_env()
E NameError: name 'setup_env' is not defined
In setup(), I want to get the return value "result" from setup_env() in conftest.py.
Could any expert guide me how to do it?
I believe that #pytest.mark.usefixtures is more meant for state alteration prior to the execution of each test. From the docs:
"Sometimes test functions do not directly need access to a fixture object."
https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
Meaning that your fixture is running at the start of each test, but your functions do not have access to it.
When your tests need access to the object returned by your fixture, it should be already populated by name when placed in conftest.py and marked with #pytest.fixture. All you need to do is then delcare the name of the fixture as an argument to your test function, like so:
https://docs.pytest.org/en/latest/fixture.html#using-fixtures-from-classes-modules-or-projects
If you prefer to do this on a class or module level, you want to change the scope of your #pytest.fixture statement, like so:
https://docs.pytest.org/en/latest/fixture.html#sharing-a-fixture-across-tests-in-a-module-or-class-session
Sorry for so many links to the docs, but I think they have good examples. Hope that clears things up.