Remove deprecated nose support

This commit is contained in:
Ran Benita 2024-01-01 15:20:38 +02:00
parent 0591569b4b
commit 0f18a7fe5e
17 changed files with 116 additions and 909 deletions

View File

@ -97,8 +97,8 @@ Features
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for - `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
managing small or parametrized long-lived test resources managing small or parametrized long-lived test resources
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial), - Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial)
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box test suites out of the box
- Python 3.8+ or PyPy3 - Python 3.8+ or PyPy3

View File

@ -44,7 +44,7 @@ Partner projects, sign up here! (by 22 March)
What does it mean to "adopt pytest"? What does it mean to "adopt pytest"?
----------------------------------------- -----------------------------------------
There can be many different definitions of "success". Pytest can run many nose_ and unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right? There can be many different definitions of "success". Pytest can run many unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
Progressive success might look like: Progressive success might look like:
@ -62,7 +62,6 @@ Progressive success might look like:
It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies. It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
.. _nose: nose.html
.. _unittest: unittest.html .. _unittest: unittest.html
.. _assert: assert.html .. _assert: assert.html
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview .. _pycmd: https://bitbucket.org/hpk42/pycmd/overview

View File

@ -44,7 +44,6 @@ How-to guides
how-to/existingtestsuite how-to/existingtestsuite
how-to/unittest how-to/unittest
how-to/nose
how-to/xunit_setup how-to/xunit_setup
how-to/bash-completion how-to/bash-completion

View File

@ -19,113 +19,6 @@ Below is a complete list of all pytest features which are considered deprecated.
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`. :class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
.. _nose-deprecation:
Support for tests written for nose
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
``nose`` has been in maintenance mode-only for years, and maintaining the plugin is not trivial as it spills
over the code base (see :issue:`9886` for more details).
setup/teardown
^^^^^^^^^^^^^^
One thing that might catch users by surprise is that plain ``setup`` and ``teardown`` methods are not pytest native,
they are in fact part of the ``nose`` support.
.. code-block:: python
class Test:
def setup(self):
self.resource = make_resource()
def teardown(self):
self.resource.close()
def test_foo(self):
...
def test_bar(self):
...
Native pytest support uses ``setup_method`` and ``teardown_method`` (see :ref:`xunit-method-setup`), so the above should be changed to:
.. code-block:: python
class Test:
def setup_method(self):
self.resource = make_resource()
def teardown_method(self):
self.resource.close()
def test_foo(self):
...
def test_bar(self):
...
This is easy to do in an entire code base by doing a simple find/replace.
@with_setup
^^^^^^^^^^^
Code using `@with_setup <with-setup-nose>`_ such as this:
.. code-block:: python
from nose.tools import with_setup
def setup_some_resource():
...
def teardown_some_resource():
...
@with_setup(setup_some_resource, teardown_some_resource)
def test_foo():
...
Will also need to be ported to a supported pytest style. One way to do it is using a fixture:
.. code-block:: python
import pytest
def setup_some_resource():
...
def teardown_some_resource():
...
@pytest.fixture
def some_resource():
setup_some_resource()
yield
teardown_some_resource()
def test_foo(some_resource):
...
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
.. _node-ctor-fspath-deprecation: .. _node-ctor-fspath-deprecation:
``fspath`` argument for Node constructors replaced with ``pathlib.Path`` ``fspath`` argument for Node constructors replaced with ``pathlib.Path``
@ -383,6 +276,115 @@ an appropriate period of deprecation has passed.
Some breaking changes which could not be deprecated are also listed. Some breaking changes which could not be deprecated are also listed.
.. _nose-deprecation:
Support for tests written for nose
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2
.. versionremoved:: 8.0
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
``nose`` has been in maintenance mode-only for years, and maintaining the plugin is not trivial as it spills
over the code base (see :issue:`9886` for more details).
setup/teardown
^^^^^^^^^^^^^^
One thing that might catch users by surprise is that plain ``setup`` and ``teardown`` methods are not pytest native,
they are in fact part of the ``nose`` support.
.. code-block:: python
class Test:
def setup(self):
self.resource = make_resource()
def teardown(self):
self.resource.close()
def test_foo(self):
...
def test_bar(self):
...
Native pytest support uses ``setup_method`` and ``teardown_method`` (see :ref:`xunit-method-setup`), so the above should be changed to:
.. code-block:: python
class Test:
def setup_method(self):
self.resource = make_resource()
def teardown_method(self):
self.resource.close()
def test_foo(self):
...
def test_bar(self):
...
This is easy to do in an entire code base by doing a simple find/replace.
@with_setup
^^^^^^^^^^^
Code using `@with_setup <with-setup-nose>`_ such as this:
.. code-block:: python
from nose.tools import with_setup
def setup_some_resource():
...
def teardown_some_resource():
...
@with_setup(setup_some_resource, teardown_some_resource)
def test_foo():
...
Will also need to be ported to a supported pytest style. One way to do it is using a fixture:
.. code-block:: python
import pytest
def setup_some_resource():
...
def teardown_some_resource():
...
@pytest.fixture
def some_resource():
setup_some_resource()
yield
teardown_some_resource()
def test_foo(some_resource):
...
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit`` Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -18,7 +18,6 @@ For basic examples, see
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples - :ref:`Fixtures <fixtures>` for basic fixture/setup examples
- :ref:`parametrize` for basic test function parametrization - :ref:`parametrize` for basic test function parametrization
- :ref:`unittest` for basic unittest integration - :ref:`unittest` for basic unittest integration
- :ref:`noseintegration` for basic nosetests integration
The following examples aim at various use cases you might encounter. The following examples aim at various use cases you might encounter.

View File

@ -85,7 +85,7 @@ style of setup/teardown functions:
In addition, pytest continues to support :ref:`xunitsetup`. You can mix In addition, pytest continues to support :ref:`xunitsetup`. You can mix
both styles, moving incrementally from classic to new style, as you both styles, moving incrementally from classic to new style, as you
prefer. You can also start out from existing :ref:`unittest.TestCase prefer. You can also start out from existing :ref:`unittest.TestCase
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects. style <unittest.TestCase>`.

View File

@ -4,8 +4,8 @@ How to use pytest with an existing test suite
============================================== ==============================================
Pytest can be used with most existing test suites, but its Pytest can be used with most existing test suites, but its
behavior differs from other test runners such as :ref:`nose <noseintegration>` or behavior differs from other test runners such as Python's
Python's default unittest framework. default unittest framework.
Before using this section you will want to :ref:`install pytest <getstarted>`. Before using this section you will want to :ref:`install pytest <getstarted>`.

View File

@ -52,7 +52,6 @@ pytest and other test systems
existingtestsuite existingtestsuite
unittest unittest
nose
xunit_setup xunit_setup
pytest development environment pytest development environment

View File

@ -1,99 +0,0 @@
.. _`noseintegration`:
How to run tests written for nose
=======================================
``pytest`` has basic support for running tests written for nose_.
.. warning::
This functionality has been deprecated and is likely to be removed in ``pytest 8.x``.
.. _nosestyle:
Usage
-------------
After :ref:`installation` type:
.. code-block:: bash
python setup.py develop # make sure tests can import our package
pytest # instead of 'nosetests'
and you should be able to run your nose style tests and
make use of pytest's capabilities.
Supported nose Idioms
----------------------
* ``setup()`` and ``teardown()`` at module/class/method level: any function or method called ``setup`` will be called during the setup phase for each test, same for ``teardown``.
* ``SkipTest`` exceptions and markers
* setup/teardown decorators
* ``__test__`` attribute on modules/classes/functions
* general usage of nose utilities
Unsupported idioms / known issues
----------------------------------
- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
are recognized only on ``unittest.TestCase`` classes but not
on plain classes. ``nose`` supports these methods also on plain
classes but pytest deliberately does not. As nose and pytest already
both support ``setup_class, teardown_class, setup_method, teardown_method``
it doesn't seem useful to duplicate the unittest-API like nose does.
If you however rather think pytest should support the unittest-spelling on
plain classes please post to :issue:`377`.
- nose imports test modules with the same import path (e.g.
``tests.test_mode``) but different file system paths
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
If you place a conftest.py file in the root directory of your project
(as determined by pytest) pytest will run tests "nose style" against
the code below that directory by adding it to your ``sys.path`` instead of
running against your installed code.
You may find yourself wanting to do this if you ran ``python setup.py install``
to set up your project, as opposed to ``python setup.py develop`` or any of
the package manager equivalents. Installing with develop in a
virtual environment like tox is recommended over this pattern.
- nose-style doctests are not collected and executed correctly,
also doctest fixtures don't work.
- no nose-configuration is recognized.
- ``yield``-based methods are
fundamentally incompatible with pytest because they don't support fixtures
properly since collection and test execution are separated.
Here is a table comparing the default supported naming conventions for both
nose and pytest.
========= ========================== ======= =====
what default naming convention pytest nose
========= ========================== ======= =====
module ``test*.py``
module ``test_*.py`` ✅ ✅
module ``*_test.py``
module ``*_tests.py``
class ``*(unittest.TestCase)`` ✅ ✅
method ``test_*`` ✅ ✅
class ``Test*``
method ``test_*``
function ``test_*``
========= ========================== ======= =====
Migrating from nose to pytest
------------------------------
`nose2pytest <https://github.com/pytest-dev/nose2pytest>`_ is a Python script
and pytest plugin to help convert Nose-based tests into pytest-based tests.
Specifically, the script transforms ``nose.tools.assert_*`` function calls into
raw assert statements, while preserving format of original arguments
as much as possible.
.. _nose: https://nose.readthedocs.io/en/latest/

View File

@ -74,7 +74,7 @@ Features
- :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources - :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box - Can run :ref:`unittest <unittest>` (including trial) test suites out of the box
- Python 3.8+ or PyPy 3 - Python 3.8+ or PyPy 3

View File

@ -69,7 +69,6 @@ testing =
attrs>=19.2.0 attrs>=19.2.0
hypothesis>=3.56 hypothesis>=3.56
mock mock
nose
pygments>=2.7.2 pygments>=2.7.2
requests requests
setuptools setuptools

View File

@ -252,7 +252,6 @@ default_plugins = essential_plugins + (
"monkeypatch", "monkeypatch",
"recwarn", "recwarn",
"pastebin", "pastebin",
"nose",
"assertion", "assertion",
"junitxml", "junitxml",
"doctest", "doctest",

View File

@ -23,21 +23,6 @@ DEPRECATED_EXTERNAL_PLUGINS = {
"pytest_faulthandler", "pytest_faulthandler",
} }
NOSE_SUPPORT = UnformattedWarning(
PytestRemovedIn8Warning,
"Support for nose tests is deprecated and will be removed in a future release.\n"
"{nodeid} is using nose method: `{method}` ({stage})\n"
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
)
NOSE_SUPPORT_METHOD = UnformattedWarning(
PytestRemovedIn8Warning,
"Support for nose tests is deprecated and will be removed in a future release.\n"
"{nodeid} is using nose-specific method: `{method}(self)`\n"
"To remove this warning, rename it to `{method}_method(self)`\n"
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
)
# This can be* removed pytest 8, but it's harmless and common, so no rush to remove. # This can be* removed pytest 8, but it's harmless and common, so no rush to remove.
# * If you're in the future: "could have been". # * If you're in the future: "could have been".

View File

@ -1,50 +0,0 @@
"""Run testsuites written for nose."""
import warnings
from _pytest.config import hookimpl
from _pytest.deprecated import NOSE_SUPPORT
from _pytest.fixtures import getfixturemarker
from _pytest.nodes import Item
from _pytest.python import Function
from _pytest.unittest import TestCaseFunction
@hookimpl(trylast=True)
def pytest_runtest_setup(item: Item) -> None:
if not isinstance(item, Function):
return
# Don't do nose style setup/teardown on direct unittest style classes.
if isinstance(item, TestCaseFunction):
return
# Capture the narrowed type of item for the teardown closure,
# see https://github.com/python/mypy/issues/2608
func = item
call_optional(func.obj, "setup", func.nodeid)
func.addfinalizer(lambda: call_optional(func.obj, "teardown", func.nodeid))
# NOTE: Module- and class-level fixtures are handled in python.py
# with `pluginmanager.has_plugin("nose")` checks.
# It would have been nicer to implement them outside of core, but
# it's not straightforward.
def call_optional(obj: object, name: str, nodeid: str) -> bool:
method = getattr(obj, name, None)
if method is None:
return False
is_fixture = getfixturemarker(method) is not None
if is_fixture:
return False
if not callable(method):
return False
# Warn about deprecation of this plugin.
method_name = getattr(method, "__name__", str(method))
warnings.warn(
NOSE_SUPPORT.format(nodeid=nodeid, method=method_name, stage=name), stacklevel=2
)
# If there are any problems allow the exception to raise rather than
# silently ignoring it.
method()
return True

View File

@ -57,7 +57,6 @@ from _pytest.config import ExitCode
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.deprecated import NOSE_SUPPORT_METHOD
from _pytest.fixtures import FixtureDef from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.fixtures import FuncFixtureInfo from _pytest.fixtures import FuncFixtureInfo
@ -596,23 +595,12 @@ class Module(nodes.File, PyCollector):
Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
other fixtures (#517). other fixtures (#517).
""" """
has_nose = self.config.pluginmanager.has_plugin("nose")
setup_module = _get_first_non_fixture_func( setup_module = _get_first_non_fixture_func(
self.obj, ("setUpModule", "setup_module") self.obj, ("setUpModule", "setup_module")
) )
if setup_module is None and has_nose:
# The name "setup" is too common - only treat as fixture if callable.
setup_module = _get_first_non_fixture_func(self.obj, ("setup",))
if not callable(setup_module):
setup_module = None
teardown_module = _get_first_non_fixture_func( teardown_module = _get_first_non_fixture_func(
self.obj, ("tearDownModule", "teardown_module") self.obj, ("tearDownModule", "teardown_module")
) )
if teardown_module is None and has_nose:
teardown_module = _get_first_non_fixture_func(self.obj, ("teardown",))
# Same as "setup" above - only treat as fixture if callable.
if not callable(teardown_module):
teardown_module = None
if setup_module is None and teardown_module is None: if setup_module is None and teardown_module is None:
return return
@ -853,21 +841,10 @@ class Class(PyCollector):
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
other fixtures (#517). other fixtures (#517).
""" """
has_nose = self.config.pluginmanager.has_plugin("nose")
setup_name = "setup_method" setup_name = "setup_method"
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
emit_nose_setup_warning = False
if setup_method is None and has_nose:
setup_name = "setup"
emit_nose_setup_warning = True
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
teardown_name = "teardown_method" teardown_name = "teardown_method"
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
emit_nose_teardown_warning = False
if teardown_method is None and has_nose:
teardown_name = "teardown"
emit_nose_teardown_warning = True
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
if setup_method is None and teardown_method is None: if setup_method is None and teardown_method is None:
return return
@ -882,24 +859,10 @@ class Class(PyCollector):
if setup_method is not None: if setup_method is not None:
func = getattr(self, setup_name) func = getattr(self, setup_name)
_call_with_optional_argument(func, method) _call_with_optional_argument(func, method)
if emit_nose_setup_warning:
warnings.warn(
NOSE_SUPPORT_METHOD.format(
nodeid=request.node.nodeid, method="setup"
),
stacklevel=2,
)
yield yield
if teardown_method is not None: if teardown_method is not None:
func = getattr(self, teardown_name) func = getattr(self, teardown_name)
_call_with_optional_argument(func, method) _call_with_optional_argument(func, method)
if emit_nose_teardown_warning:
warnings.warn(
NOSE_SUPPORT_METHOD.format(
nodeid=request.node.nodeid, method="teardown"
),
stacklevel=2,
)
self.obj.__pytest_setup_method = xunit_setup_method_fixture self.obj.__pytest_setup_method = xunit_setup_method_fixture

View File

@ -188,62 +188,3 @@ def test_fixture_disallowed_between_marks():
raise NotImplementedError() raise NotImplementedError()
assert len(record) == 2 # one for each mark decorator assert len(record) == 2 # one for each mark decorator
@pytest.mark.filterwarnings("default")
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
pytest.importorskip("nose")
pytester.makepyfile(
"""
from nose.tools import with_setup
def setup_fn_no_op():
...
def teardown_fn_no_op():
...
@with_setup(setup_fn_no_op, teardown_fn_no_op)
def test_omits_warnings():
...
"""
)
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
message = [
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `teardown_fn_no_op` (teardown)",
]
output.stdout.fnmatch_lines(message)
output.assert_outcomes(passed=1)
@pytest.mark.filterwarnings("default")
def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None:
pytest.importorskip("nose")
pytester.makepyfile(
"""
class Test:
def setup(self):
...
def teardown(self):
...
def test(self):
...
"""
)
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
message = [
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
"*To remove this warning, rename it to `setup_method(self)`",
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `teardown(self)`",
"*To remove this warning, rename it to `teardown_method(self)`",
]
output.stdout.fnmatch_lines(message)
output.assert_outcomes(passed=1)

View File

@ -1,529 +0,0 @@
import pytest
from _pytest.pytester import Pytester
def setup_module(mod):
mod.nose = pytest.importorskip("nose")
def test_nose_setup(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
values = []
from nose.tools import with_setup
@with_setup(lambda: values.append(1), lambda: values.append(2))
def test_hello():
assert values == [1]
def test_world():
assert values == [1,2]
test_hello.setup = lambda: values.append(1)
test_hello.teardown = lambda: values.append(2)
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.assert_outcomes(passed=2)
def test_setup_func_with_setup_decorator() -> None:
from _pytest.nose import call_optional
values = []
class A:
@pytest.fixture(autouse=True)
def f(self):
values.append(1)
call_optional(A(), "f", "A.f")
assert not values
def test_setup_func_not_callable() -> None:
from _pytest.nose import call_optional
class A:
f = 1
call_optional(A(), "f", "A.f")
def test_nose_setup_func(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
from nose.tools import with_setup
values = []
def my_setup():
a = 1
values.append(a)
def my_teardown():
b = 2
values.append(b)
@with_setup(my_setup, my_teardown)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.assert_outcomes(passed=2)
def test_nose_setup_func_failure(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
from nose.tools import with_setup
values = []
my_setup = lambda x: 1
my_teardown = lambda x: 2
@with_setup(my_setup, my_teardown)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*TypeError: <lambda>()*"])
def test_nose_setup_func_failure_2(pytester: Pytester) -> None:
pytester.makepyfile(
"""
values = []
my_setup = 1
my_teardown = 2
def test_hello():
assert values == []
test_hello.setup = my_setup
test_hello.teardown = my_teardown
"""
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=1)
def test_nose_setup_partial(pytester: Pytester) -> None:
pytest.importorskip("functools")
p = pytester.makepyfile(
"""
from functools import partial
values = []
def my_setup(x):
a = x
values.append(a)
def my_teardown(x):
b = x
values.append(b)
my_setup_partial = partial(my_setup, 1)
my_teardown_partial = partial(my_teardown, 2)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
test_hello.setup = my_setup_partial
test_hello.teardown = my_teardown_partial
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*2 passed*"])
def test_module_level_setup(pytester: Pytester) -> None:
pytester.makepyfile(
"""
from nose.tools import with_setup
items = {}
def setup():
items.setdefault("setup", []).append("up")
def teardown():
items.setdefault("setup", []).append("down")
def setup2():
items.setdefault("setup2", []).append("up")
def teardown2():
items.setdefault("setup2", []).append("down")
def test_setup_module_setup():
assert items["setup"] == ["up"]
def test_setup_module_setup_again():
assert items["setup"] == ["up"]
@with_setup(setup2, teardown2)
def test_local_setup():
assert items["setup"] == ["up"]
assert items["setup2"] == ["up"]
@with_setup(setup2, teardown2)
def test_local_setup_again():
assert items["setup"] == ["up"]
assert items["setup2"] == ["up", "down", "up"]
"""
)
result = pytester.runpytest(
"-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*4 passed*"])
def test_nose_style_setup_teardown(pytester: Pytester) -> None:
pytester.makepyfile(
"""
values = []
def setup_module():
values.append(1)
def teardown_module():
del values[0]
def test_hello():
assert values == [1]
def test_world():
assert values == [1]
"""
)
result = pytester.runpytest("-p", "nose")
result.stdout.fnmatch_lines(["*2 passed*"])
def test_fixtures_nose_setup_issue8394(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def setup_module():
pass
def teardown_module():
pass
def setup_function(func):
pass
def teardown_function(func):
pass
def test_world():
pass
class Test(object):
def setup_class(cls):
pass
def teardown_class(cls):
pass
def setup_method(self, meth):
pass
def teardown_method(self, meth):
pass
def test_method(self): pass
"""
)
match = "*no docstring available*"
result = pytester.runpytest("--fixtures")
assert result.ret == 0
result.stdout.no_fnmatch_line(match)
result = pytester.runpytest("--fixtures", "-v")
assert result.ret == 0
result.stdout.fnmatch_lines([match, match, match, match])
def test_nose_setup_ordering(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def setup_module(mod):
mod.visited = True
class TestClass(object):
def setup(self):
assert visited
self.visited_cls = True
def test_first(self):
assert visited
assert self.visited_cls
"""
)
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(["*1 passed*"])
def test_apiwrapper_problem_issue260(pytester: Pytester) -> None:
# this would end up trying a call an optional teardown on the class
# for plain unittests we don't want nose behaviour
pytester.makepyfile(
"""
import unittest
class TestCase(unittest.TestCase):
def setup(self):
#should not be called in unittest testcases
assert 0, 'setup'
def teardown(self):
#should not be called in unittest testcases
assert 0, 'teardown'
def setUp(self):
print('setup')
def tearDown(self):
print('teardown')
def test_fun(self):
pass
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_setup_teardown_linking_issue265(pytester: Pytester) -> None:
# we accidentally didn't integrate nose setupstate with normal setupstate
# this test ensures that won't happen again
pytester.makepyfile(
'''
import pytest
class TestGeneric(object):
def test_nothing(self):
"""Tests the API of the implementation (for generic and specialized)."""
@pytest.mark.skipif("True", reason=
"Skip tests to check if teardown is skipped as well.")
class TestSkipTeardown(TestGeneric):
def setup(self):
"""Sets up my specialized implementation for $COOL_PLATFORM."""
raise Exception("should not call setup for skipped tests")
def teardown(self):
"""Undoes the setup."""
raise Exception("should not call teardown for skipped tests")
'''
)
reprec = pytester.runpytest()
reprec.assert_outcomes(passed=1, skipped=1)
def test_SkipTest_during_collection(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose
raise nose.SkipTest("during collection")
def test_failing():
assert False
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(skipped=1, warnings=0)
def test_SkipTest_in_test(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose
def test_skipping():
raise nose.SkipTest("in test")
"""
)
reprec = pytester.inline_run()
reprec.assertoutcome(skipped=1)
def test_istest_function_decorator(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose.tools
@nose.tools.istest
def not_test_prefix():
pass
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(passed=1)
def test_nottest_function_decorator(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose.tools
@nose.tools.nottest
def test_prefix():
pass
"""
)
reprec = pytester.inline_run()
assert not reprec.getfailedcollections()
calls = reprec.getreports("pytest_runtest_logreport")
assert not calls
def test_istest_class_decorator(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose.tools
@nose.tools.istest
class NotTestPrefix(object):
def test_method(self):
pass
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(passed=1)
def test_nottest_class_decorator(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose.tools
@nose.tools.nottest
class TestPrefix(object):
def test_method(self):
pass
"""
)
reprec = pytester.inline_run()
assert not reprec.getfailedcollections()
calls = reprec.getreports("pytest_runtest_logreport")
assert not calls
def test_skip_test_with_unicode(pytester: Pytester) -> None:
pytester.makepyfile(
"""\
import unittest
class TestClass():
def test_io(self):
raise unittest.SkipTest('😊')
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["* 1 skipped *"])
def test_raises(pytester: Pytester) -> None:
pytester.makepyfile(
"""
from nose.tools import raises
@raises(RuntimeError)
def test_raises_runtimeerror():
raise RuntimeError
@raises(Exception)
def test_raises_baseexception_not_caught():
raise BaseException
@raises(BaseException)
def test_raises_baseexception_caught():
raise BaseException
"""
)
result = pytester.runpytest("-vv")
result.stdout.fnmatch_lines(
[
"test_raises.py::test_raises_runtimeerror PASSED*",
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
"test_raises.py::test_raises_baseexception_caught PASSED*",
"*= FAILURES =*",
"*_ test_raises_baseexception_not_caught _*",
"",
"arg = (), kw = {}",
"",
" def newfunc(*arg, **kw):",
" try:",
"> func(*arg, **kw)",
"",
"*/nose/*: ",
"_ _ *",
"",
" @raises(Exception)",
" def test_raises_baseexception_not_caught():",
"> raise BaseException",
"E BaseException",
"",
"test_raises.py:9: BaseException",
"* 1 failed, 2 passed *",
]
)
def test_nose_setup_skipped_if_non_callable(pytester: Pytester) -> None:
"""Regression test for #9391."""
p = pytester.makepyfile(
__init__="",
setup="""
""",
teardown="""
""",
test_it="""
from . import setup, teardown
def test_it():
pass
""",
)
result = pytester.runpytest(p.parent, "-p", "nose")
assert result.ret == 0
@pytest.mark.parametrize("fixture_name", ("teardown", "teardown_class"))
def test_teardown_fixture_not_called_directly(fixture_name, pytester: Pytester) -> None:
"""Regression test for #10597."""
p = pytester.makepyfile(
f"""
import pytest
class TestHello:
@pytest.fixture
def {fixture_name}(self):
yield
def test_hello(self, {fixture_name}):
assert True
"""
)
result = pytester.runpytest(p, "-p", "nose")
assert result.ret == 0