Calling fixtures directly is now an error instead of a warning

Fix #4545
This commit is contained in:
Bruno Oliveira 2018-12-18 21:05:48 -02:00
parent 8563364d8b
commit 0115766df3
6 changed files with 79 additions and 81 deletions

View File

@ -0,0 +1,3 @@
Calling fixtures directly is now always an error instead of a warning.
See our `docs <https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly>`__ on information on how to update your code.

View File

@ -72,46 +72,6 @@ Becomes:
Calling fixtures directly
~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.7
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
For example:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell():
cell = cell()
cell.make_full()
return cell
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
In those cases just request the function directly in the dependent fixture:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell(cell):
cell.make_full()
return cell
``Node.get_marker``
~~~~~~~~~~~~~~~~~~~
@ -353,6 +313,58 @@ By passing a string, users expect that pytest will interpret that command-line u
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
Calling fixtures directly
~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 4.0.*
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
For example:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell():
cell = cell()
cell.make_full()
return cell
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
In those cases just request the function directly in the dependent fixture:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell(cell):
cell.make_full()
return cell
Alternatively if the fixture function is called multiple times inside a test (making it hard to apply the above pattern) or
if you would like to make minimal changes to the code, you can create a fixture which calls the original function together
with the ``name`` parameter:
.. code-block:: python
def cell():
return ...
@pytest.fixture(name="cell")
def cell_fixture():
return cell()
``yield`` tests
~~~~~~~~~~~~~~~

View File

@ -20,11 +20,11 @@ from _pytest.warning_types import UnformattedWarning
YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored"
FIXTURE_FUNCTION_CALL = UnformattedWarning(
RemovedInPytest4Warning,
'Fixture "{name}" called directly. Fixtures are not meant to be called directly, '
"are created automatically when test functions request them as parameters. "
"See https://docs.pytest.org/en/latest/fixture.html for more information.",
FIXTURE_FUNCTION_CALL = (
'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n'
"but are created automatically when test functions request them as parameters.\n"
"See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n"
"https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly about how to update your code."
)
FIXTURE_NAMED_REQUEST = PytestDeprecationWarning(

View File

@ -942,34 +942,17 @@ def _ensure_immutable_ids(ids):
return tuple(ids)
def wrap_function_to_warning_if_called_directly(function, fixture_marker):
"""Wrap the given fixture function so we can issue warnings about it being called directly, instead of
used as an argument in a test function.
def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
"""Wrap the given fixture function so we can raise an error about it being called directly,
instead of used as an argument in a test function.
"""
is_yield_function = is_generator(function)
warning = FIXTURE_FUNCTION_CALL.format(
message = FIXTURE_FUNCTION_CALL.format(
name=fixture_marker.name or function.__name__
)
if is_yield_function:
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
for x in function(*args, **kwargs):
yield x
else:
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
return function(*args, **kwargs)
if six.PY2:
result.__wrapped__ = function
@six.wraps(function)
def result(*args, **kwargs):
fail(message, pytrace=False)
# keep reference to the original function in our own custom attribute so we don't unwrap
# further than this point and lose useful wrappings like @mock.patch (#3774)
@ -995,7 +978,7 @@ class FixtureFunctionMarker(object):
"fixture is being applied more than once to the same function"
)
function = wrap_function_to_warning_if_called_directly(function, self)
function = wrap_function_to_error_out_if_called_directly(function, self)
name = self.name or function.__name__
if name == "request":

View File

@ -223,17 +223,6 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives(
assert msg not in res.stdout.str()
def test_call_fixture_function_deprecated():
"""Check if a warning is raised if a fixture function is called directly (#3661)"""
@pytest.fixture
def fix():
return 1
with pytest.deprecated_call():
assert fix() == 1
def test_fixture_named_request(testdir):
testdir.copy_example()
result = testdir.runpytest()

View File

@ -3850,3 +3850,14 @@ class TestScopeOrdering(object):
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=2)
def test_call_fixture_function_error():
"""Check if an error is raised if a fixture function is called directly (#4545)"""
@pytest.fixture
def fix():
return 1
with pytest.raises(pytest.fail.Exception):
assert fix() == 1