nose2: the unittest-based successor to nose, and the README that tells you to use pytest
The successor to nose, based on unittest2
At a glance
- What is it?
- nose2 extends the standard library's unittest rather than replacing it, so a project can adopt it without rewriting its tests, and it adds plugins, parameterised cases and readable assertion output on top. The interesting part of its README is the section that compares it to pytest and suggests you consider that instead for a new project.
- Who is it for?
- Adopt nose2 if you have an existing unittest suite, or a shared library whose tests you cannot rewrite, because nose2 runs standard unittest test cases and adds plugins, parameterisation and readable failures without asking you to change the tests. Do not adopt it for a new project if you are free to choose, because the project itself says pytest is excellent, has more maintainers and more users, and recommends considering it.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It extends unittest rather than replacing it
nose2 originated as the successor to nose, and the package description in the project file is the shortest summary of the design: unittest with plugins.
That choice shapes everything about the project. Because nose2 is built on unittest, the documentation says you can start from the Python standard library's documentation for unittest and then use nose2 to add value on top. A test written in the usual style, a class deriving from the standard test case, is a valid nose2 test with no changes at all.
The discovery rule is stated plainly. nose2 looks for tests in Python files whose names start with test and runs every test function it discovers. That is a narrower rule than some other runners use, and it is a rule you can predict, which matters when a suite starts behaving strangely in continuous integration.
The project also maintains the inheritance from the earlier unittest2 lineage, which is why the repository description mentions it. The code lives in a package directory in the repository, the documentation is built with Sphinx and published at a documentation site, and the licence in the project file is a two clause BSD, even though the repository listing records the licence as one that GitHub cannot classify. The author is named in the metadata, and there is a mailing list and a channel for discussion.
The last push was on 2026-10-19, the project is marked as a beta in its classifiers, and the repository publishes no GitHub releases, so the version history lives in the changelog and on the package index.
The comparison with pytest is a recommendation against itself
The README has a section comparing nose2 with pytest, and it is worth quoting in full because of how unusual it is.
It says pytest is an excellent test framework and that the maintainers encourage users to consider it for new projects. It adds that pytest has a bigger team of maintainers and a larger community of users.
That is a project telling its own audience to use something else, and it is a sign of how the maintainer sees the niche. nose2 is not trying to win the general purpose test runner contest. It is the option for a team that has decided, for reasons of their own, that they want the standard library's semantics, and nose2 gives them the standard library plus plugins without a rewrite.
The comparison with nose is more cautious, and the reason is that nose2 is not a drop-in replacement for it. The README says nose2 is a distinct project and does not support all of the behaviours of nose, and it points to a page with a thorough rundown of the differences. For anyone migrating a suite, that page is the work item, and the project's own admission that it is not complete is a reason to read it rather than to try it and hope.
So the two comparisons together define the audience: existing nose users who need to understand what changed, and existing unittest users who want more, both of whom the maintainer would still prefer to see move to pytest if they have the choice.
A quickstart that requires no changes to your tests
The quickstart section gives a plain unittest example and then shows what the extra features look like.
The simple case is a module with a test class deriving from the standard test case and an assertion that a string method returns the expected value, run from the command line with the verbose flag, and the output is the familiar one, a test name, the word ok, a count of tests run and the word OK.
The second example shows where nose2 adds something. It imports a params helper from the nose2 tools package and decorates a test function with it, giving the function three values, and each value becomes a separate test case. The expected result is that one of the three passes and two fail, and the verbose output lists them individually, which is the behaviour you want from a parameterised test: three named results rather than one result for a loop.
The run also uses the pretty assertion flag, and the difference in the output is dramatic. Instead of a traceback with a value dump, the failure is shown with the expression that failed, the values involved and the built-in method that was called, so the report reads like the assertion rather than like a stack trace.
Running a suite is then one command, and the value of that is mostly that it is the same command everywhere, in a local shell, in a continuous integration job and in a developer's terminal:
nose2 -vVersion numbering, and the closed Python 2 line
The versions and support section is precise about two things, the version scheme and the interpreter range.
nose2 versions are numbered 0.MAJOR.MINOR. Minor releases contain bug fixes or smaller features, and major features or backwards incompatible changes are done in major releases. That is an unusual scheme, and the practical effect is that the third number never moves, so a user who wants only fixes pins the minor number, and a user who wants a new feature has to move the second one and accept the possibility of breakage. The changelog is the record of what changed.
The interpreter story is the part that has clearly moved. nose2 requires Python 3, and it supports all versions currently supported by the CPython team, with an aim to support PyPy and CPython betas as well. The project file asks for Python 3.9 or newer and the classifiers list 3.9 through 3.15, which is a wider spread than most projects claim.
Python 2 is finished. The 0.12 line of releases contains the last versions which supported Python 2, the README says that support line is considered end of life, and there is a note that fixes to that line may still be accepted on an as-needed basis for a short while as the Python 3 only line gets started. That note is dated, and reading it now tells you something about how the project has aged: the migration is complete, and the old branch is being kept warm rather than developed.
A console script, two optional extras, and a version read from the source
The packaging is a small part of the project but it shows how the tool is meant to be installed and extended.
The project file defines a single console script, nose2, which points at the discovery entry point in the package, so installing the package puts a nose2 command on the path. The optional dependencies are two groups. One installs the coverage package, which is what the coverage plugin needs. The other is the documentation group, with the documentation generator, its issue tracker extension and the theme.
The version is dynamic in two places. The project file marks it as dynamic, and the setuptools configuration says where to find it, pointing at an attribute in the package, and the Makefile greps the same attribute out of the package source to get the version for tagging. So there is one line that has to change to cut a release, and both the build and the tag agree about it.
The build backend is setuptools with a one line setup file that just calls it, and the source is formatted with a consistent style, the import sorting configured to match the formatter, and the type checker configured for the package directory with strict mode commented out rather than enabled, which is a deliberate choice for a project that supports a wide range of interpreter versions.
The release process is a signed tag and a build, driven by a make target that extracts the version, tags it with the message, and then runs the build and publish environments under the task runner.
A maintainer workflow built on tox, pre-commit and a signed tag
The repository layout is small and tells you what the project's own tooling looks like.
The Makefile is the entry point and it does four things. Linting runs a lint environment of the task runner, testing runs the default environment, the documentation has its own environment, and the build has another. The clean target drops the task runner's environment directory and delegates the documentation cleanup to the documentation directory itself. The release target signs a tag with the version and then runs the build and publish environments.
Around that there is a task runner configuration with the environments for testing, linting, documentation and publishing, a flake8 configuration, a coverage configuration, a pre-commit configuration, and a readthedocs configuration for the documentation build. The repository also has a unit test configuration file at its root, an authors file, a changelog, a contributing guide and a separate releasing guide, which is a small sign of a project that takes its own process seriously.
Two details are worth noting. The documentation is written in reStructuredText, which is why the README itself is an rst file and uses directive syntax, and the documentation sources live in their own directory with a Makefile of their own, since the root clean target delegates to it. And the mailing list is the discussion channel, with the README pointing new contributors at a contributing guide in the repository rather than at a wiki page somewhere else.
For a project that is a successor to a tool many people have muscle memory for, that combination of a small make file, a task runner and a signed tag is a sane, low ceremony way to keep releases coming.
Editorial conclusion
Adopt nose2 if you have an existing unittest suite, or a shared library whose tests you cannot rewrite, because nose2 runs standard unittest test cases and adds plugins, parameterisation and readable failures without asking you to change the tests. Do not adopt it for a new project if you are free to choose, because the project itself says pytest is excellent, has more maintainers and more users, and recommends considering it. Before you migrate a suite from nose, read the differences page, because nose2 is a distinct project that does not support all of the behaviours of nose, and decide whether the plugin system is worth the extra moving parts for your team.
Frequently asked questions
What is nose2 and how does it relate to unittest?
nose2 extends the standard library's unittest rather than replacing it, so a normal unittest test is a valid nose2 test. It adds plugins, parameterised tests through a params helper and readable assertion output, and it is the successor to the older nose project.
Is nose2 a drop-in replacement for nose?
No. The README says nose2 is a distinct project and does not support all of the behaviours of nose, and it points to a page with a thorough rundown of the differences. Anyone migrating a suite should read that page rather than assume the behaviour carries over.
What does the nose2 README say about pytest?
It says pytest is an excellent test framework and that the maintainers encourage users to consider it for new projects, noting that pytest has a bigger team of maintainers and a larger community of users. The project positions itself for existing unittest and nose users rather than for greenfield projects.
Which Python versions does nose2 support?
nose2 requires Python 3, supports all versions currently supported by the CPython team, and aims to support PyPy and CPython betas. The project file asks for Python 3.9 or newer and the classifiers list 3.9 through 3.15, and Python 2 support ended with the 0.12 release line.
How is a nose2 release versioned?
nose2 versions are numbered 0.MAJOR.MINOR. Minor releases carry bug fixes or smaller features, and major features or backwards incompatible changes happen in major releases. The Makefile reads the version from the package source, signs a tag with it and then builds and publishes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nose-devs-nose2)Was this useful?