Nose2pytest
Scripts to convert Python Nose tests to PyTest
Install / Use
/learn @pytest-dev/Nose2pytestREADME
.. image:: https://badge.fury.io/py/nose2pytest.svg :target: https://badge.fury.io/py/nose2pytest .. image:: https://github.com/pytest-dev/nose2pytest/workflows/Test/badge.svg :target: https://github.com/pytest-dev/nose2pytest/actions
.. contents::
Overview
This package provides 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 the format of original arguments as much as possible. For example, the script:
.. code-block:: python
assert_true(a, msg) assert_greater(a, b, msg)
gets converted to:
.. code-block:: python
assert a, msg assert a > b, msg
A small subset of nose.tools.assert_* function calls are not
transformed because there is no raw assert statement equivalent, or the equivalent would be hard to
maintain. They are provided as functions in the pytest namespace via pytest's plugin system.
Running
For a one-time conversion use the shell command ::
pipx run --python 3.11 nose2pytest path/to/dir/with/python_files
This will find all .py files in the folder tree starting at path/to/dir/with/python_files and
overwrite the original (assuming most users will be running this on a version-controlled code base, this is
almost always what would be most convenient). Type nose2pytest -h for other options, such as -v.
Installation
For doing multiple conversions use the shell command ::
pipx install --python 3.11 nose2pytest
For each conversion use the shell command ::
nose2pytest path/to/dir/with/python_files
Motivation
I have used Nose for years and it is a great tool. However, to get good test failure diagnostics with Nose you
ought to use the assert_*() functions from nose.tools. Although they provide very good diagnostics, they
are not as convenient to use as raw assertions, since you have to decide beforehand what type of assertion you
are going to write: an identity comparison to None, a truth check, a falseness check, an identity comparison to another
object, etc. Just being able to write a raw assertion, and still get good diagnostics on failure as done by
pytest, is really nice. This is a main reason for using pytest for me. Another reason is the design of fixtures
in pytest.
Switching an existing test suite from Nose to pytest is feasible even without nose2pytest, as it requires relatively little work: relatively as in, you will probably only need a few modifications, all achievable manually, to get the same test coverage and results. A few gotchas:
- test classes that have
__init__will be ignored, those will have to be moved (usually, into class'ssetup_class()) - the
setup.cfgmay have to be edited since test discovery rules are slightly more strict with pytest - the order of tests may be different, but in general, that should not matter
- all test modules are imported up-front, so some test modules may need adjustment such as moving some
code from the top of the test module into its
setup_module()
Once the above has been done to an existing code base, you don't really have to do anything else. However, your test
suite now has an additional third-party test dependency (Nose), just because of those assert_* functions used all
over the place. Moreover, there is no longer one obvious way to do things in your test suite: existing test code
uses nose.tools.assert_* functions, yet with pytest you can use raw assertions. If you add tests, which of
these two approaches should a developer use? If you modify existing tests, should new assertions use raw assert?
Should the remaining test method, test class, or test module be updated? A test module can contain hundreds of
calls to nose.tools.assert_* functions, is a developer to manually go through each one to convert it? Painful and
error-prone, in general not feasible to do manually.
This is why I developed nose2pytest: I wanted to migrate my pypubsub project's test suite from Nose to pytest, but also have only pytest as a dependency, and have one obvious way to write assertions in the test suite.
Requirements
I expect nose2pytest script to run with supported versions of CPython <= v3.11, on any OS supported by a version of Python that has lib2to3 compatible with fissix. I expect it to succeed even with quite old versions of Nose (even prior to 1.0 which came out ca. 2010) and with the new Nose2 test driver.
The pytest package namespace will be extended with assert_ functions that are not converted by the script
only if, err, you have pytest installed!
Status
The package has been used on over 5000 assert_*() function calls, among which the pypubsub test suite.
I consider it stable, but I have only used it on my code, and code by a few other developers. Feedback on
results of conversions would be most appreciated (such as version information and number of assert statements
converted).
The following conversions have been implemented:
============================================ ================================================================= Function Statement ============================================ ================================================================= assert_true(a[, msg]) assert a[, msg] assert_false(a[, msg]) assert not a[, msg] assert_is_none(a[, msg]) assert a is None[, msg] assert_is_not_none(a[, msg]) assert a is not None[, msg]
assert_equal(a,b[, msg]) assert a == b[, msg] assert_not_equal(a,b[, msg]) assert a != b[, msg] assert_list_equal(a,b[, msg]) assert a == b[, msg] assert_dict_equal(a,b[, msg]) assert a == b[, msg] assert_set_equal(a,b[, msg]) assert a == b[, msg] assert_sequence_equal(a,b[, msg]) assert a == b[, msg] assert_tuple_equal(a,b[, msg]) assert a == b[, msg] assert_multi_line_equal(a,b[, msg]) assert a == b[, msg] assert_greater(a,b[, msg]) assert a > b[, msg] assert_greater_equal(a,b[, msg]) assert a >= b[, msg] assert_less(a,b[, msg]) assert a < b[, msg] assert_less_equal(a,b[, msg]) assert a <= b[, msg] assert_in(a,b[, msg]) assert a in b[, msg] assert_not_in(a,b[, msg]) assert a not in b[, msg] assert_is(a,b[, msg]) assert a is b[, msg] assert_is_not(a,b[, msg]) assert a is not b[, msg]
assert_is_instance(a,b[, msg]) assert isinstance(a, b)[, msg] assert_count_equal(a,b[, msg]) assert collections.Counter(a) == collections.Counter(b)[, msg] assert_not_regex(a,b[, msg]) assert not re.search(b, a)[, msg] assert_regex(a,b[, msg]) assert re.search(b, a)[, msg]
assert_almost_equal(a,b[, msg]) assert a == pytest.approx(b, abs=1e-7)[, msg] assert_almost_equal(a,b, delta[, msg]) assert a == pytest.approx(b, abs=delta)[, msg] assert_almost_equal(a, b, places[, msg]) assert a == pytest.approx(b, abs=1e-places)[, msg] assert_not_almost_equal(a,b[, msg]) assert a != pytest.approx(b, abs=1e-7)[, msg] assert_not_almost_equal(a,b, delta[, msg]) assert a != pytest.approx(b, abs=delta)[, msg] assert_not_almost_equal(a,b, places[, msg]) assert a != pytest.approx(b, abs=1e-places)[, msg] ============================================ =================================================================
The script adds parentheses around a and/or b if operator precedence would change the interpretation of the
expression or involves newline. For example:
.. code-block:: python
assert_true(some-long-expression-a in some-long-expression-b, msg) assert_equal(a == b, b == c), msg
gets converted to:
.. code-block:: python
assert (some-long-expression-a in some-long-expression-b), msg assert (a == b) == (b == c), msg
Not every assert_* function from nose.tools is converted by nose2pytest:
-
Some Nose functions can be handled via a global search-replace, so a fixer was not a necessity:
assert_raises: replace withpytest.raisesassert_warns: replace withpytest.warns
-
Some Nose functions could be transformed but the readability would be decreased:
assert_dict_contains_subset(a,b)->assert set(b.keys()) >= a.keys() and {k: b[k] for k in a if k in b} == a
The nose2pytest distribution contains a module,
assert_tools.pywhich defines these utility functions to contain the equivalent raw assert statement. Copy the module into your test folder or into the pytest package and change your test code'sfrom nose.tools import ...statements accordingly. pytest introspection will provide error information on assertion failure. -
Some Nose functions don't have a one-line assert statement equivalent, they have to remain utility functions:
assert_raises_regexassert_raises_regexp# deprecated by Noseassert_regexp_matches# deprecated by Noseassert_warns_regex
These functions are available in
assert_tools.pyof nose2pytest distribution, and are imported as is fromunittest.TestCase(but renamed as per Nose). Copy the module into your test fo
Related Skills
node-connect
340.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
340.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.2kCommit, push, and open a PR
