Remove deprecated nose support
This commit is contained in:
parent
0591569b4b
commit
0f18a7fe5e
|
@ -97,8 +97,8 @@ Features
|
|||
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
|
||||
managing small or parametrized long-lived test resources
|
||||
|
||||
- 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
|
||||
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial)
|
||||
test suites out of the box
|
||||
|
||||
- Python 3.8+ or PyPy3
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ Partner projects, sign up here! (by 22 March)
|
|||
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:
|
||||
|
||||
|
@ -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.
|
||||
|
||||
.. _nose: nose.html
|
||||
.. _unittest: unittest.html
|
||||
.. _assert: assert.html
|
||||
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
|
||||
|
|
|
@ -44,7 +44,6 @@ How-to guides
|
|||
|
||||
how-to/existingtestsuite
|
||||
how-to/unittest
|
||||
how-to/nose
|
||||
how-to/xunit_setup
|
||||
|
||||
how-to/bash-completion
|
||||
|
|
|
@ -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>`.
|
||||
|
||||
|
||||
.. _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:
|
||||
|
||||
``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.
|
||||
|
||||
.. _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``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ For basic examples, see
|
|||
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples
|
||||
- :ref:`parametrize` for basic test function parametrization
|
||||
- :ref:`unittest` for basic unittest integration
|
||||
- :ref:`noseintegration` for basic nosetests integration
|
||||
|
||||
The following examples aim at various use cases you might encounter.
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ style of setup/teardown functions:
|
|||
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
||||
both styles, moving incrementally from classic to new style, as you
|
||||
prefer. You can also start out from existing :ref:`unittest.TestCase
|
||||
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
||||
style <unittest.TestCase>`.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ How to use pytest with an existing test suite
|
|||
==============================================
|
||||
|
||||
Pytest can be used with most existing test suites, but its
|
||||
behavior differs from other test runners such as :ref:`nose <noseintegration>` or
|
||||
Python's default unittest framework.
|
||||
behavior differs from other test runners such as Python's
|
||||
default unittest framework.
|
||||
|
||||
Before using this section you will want to :ref:`install pytest <getstarted>`.
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ pytest and other test systems
|
|||
|
||||
existingtestsuite
|
||||
unittest
|
||||
nose
|
||||
xunit_setup
|
||||
|
||||
pytest development environment
|
||||
|
|
|
@ -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/
|
|
@ -74,7 +74,7 @@ Features
|
|||
|
||||
- :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
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@ testing =
|
|||
attrs>=19.2.0
|
||||
hypothesis>=3.56
|
||||
mock
|
||||
nose
|
||||
pygments>=2.7.2
|
||||
requests
|
||||
setuptools
|
||||
|
|
|
@ -252,7 +252,6 @@ default_plugins = essential_plugins + (
|
|||
"monkeypatch",
|
||||
"recwarn",
|
||||
"pastebin",
|
||||
"nose",
|
||||
"assertion",
|
||||
"junitxml",
|
||||
"doctest",
|
||||
|
|
|
@ -23,21 +23,6 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
|||
"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.
|
||||
# * If you're in the future: "could have been".
|
||||
|
|
|
@ -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
|
|
@ -57,7 +57,6 @@ from _pytest.config import ExitCode
|
|||
from _pytest.config import hookimpl
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.deprecated import check_ispytest
|
||||
from _pytest.deprecated import NOSE_SUPPORT_METHOD
|
||||
from _pytest.fixtures import FixtureDef
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
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
|
||||
other fixtures (#517).
|
||||
"""
|
||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
||||
setup_module = _get_first_non_fixture_func(
|
||||
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(
|
||||
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:
|
||||
return
|
||||
|
@ -853,21 +841,10 @@ class Class(PyCollector):
|
|||
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
|
||||
other fixtures (#517).
|
||||
"""
|
||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
||||
setup_name = "setup_method"
|
||||
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_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:
|
||||
return
|
||||
|
||||
|
@ -882,24 +859,10 @@ class Class(PyCollector):
|
|||
if setup_method is not None:
|
||||
func = getattr(self, setup_name)
|
||||
_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
|
||||
if teardown_method is not None:
|
||||
func = getattr(self, teardown_name)
|
||||
_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
|
||||
|
||||
|
|
|
@ -188,62 +188,3 @@ def test_fixture_disallowed_between_marks():
|
|||
raise NotImplementedError()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue