Skip to content

Commit

Permalink
Calling fixtures directly is now an error instead of a warning
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Dec 18, 2018
1 parent 8563364 commit 9494c69
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 59 deletions.
3 changes: 3 additions & 0 deletions changelog/4545.removal.rst
Original file line number Diff line number Diff line change
@@ -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.
92 changes: 52 additions & 40 deletions doc/en/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -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
~~~~~~~~~~~~~~~
Expand Down
10 changes: 5 additions & 5 deletions src/_pytest/deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
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(
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
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__
)

Expand All @@ -956,7 +956,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
fail(message, pytrace=False)
for x in function(*args, **kwargs):
yield x

Expand All @@ -965,7 +965,7 @@ def result(*args, **kwargs):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
fail(message, pytrace=False)
return function(*args, **kwargs)

if six.PY2:
Expand Down
11 changes: 0 additions & 11 deletions testing/deprecated_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,6 @@ def test_func():
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()
Expand Down
11 changes: 11 additions & 0 deletions testing/python/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -3850,3 +3850,14 @@ def test_2(fix):
)
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 (#3661)"""

@pytest.fixture
def fix():
return 1

with pytest.raises(pytest.fail.Exception):
assert fix() == 1

0 comments on commit 9494c69

Please sign in to comment.