nose2-tests
Configures and runs nose2 - successor to nose (the original Python test discovery library, end-of-life 2015) and an alternative to pytest's discovery model; supports plugin architecture, layers (per-test-class setUp/tearDown shared across modules), parameterized tests via `nose2.tools.params`, multi-process parallelism via mp plugin. Use when migrating from legacy nose1 codebases or when the team prefers nose2's plugin model over pytest.
Install with skills.sh (any agent)
npx skills add testland/qa --skill nose2-testsnose2-tests
Overview
Per docs.nose2.io (opens in new window):
nose2 is the successor to nose (the original third-party Python test discovery library; reached end-of-life in 2015 - do NOT use nose1 in new projects). nose2 inherits nose's discovery model plus a plugin architecture.
Modern recommendation: prefer pytest-tests for new work. nose2 fits two cases:
When to use
Step 1 - Install
pip install nose2Step 2 - First test
nose2 supports both unittest-style TestCase classes + simple function tests:
# test_sum.py
import unittest
def sum(a, b):
return a + b
class TestSum(unittest.TestCase):
def test_adds(self):
self.assertEqual(sum(1, 2), 3)
# Function-style also works
def test_sum_function():
assert sum(2, 3) == 5Run:
nose2 # discover from cwd
nose2 -v # verbose
nose2 tests.test_sum # specific module
nose2 tests.test_sum.TestSum # specific classStep 3 - Configuration
unittest.cfg or nose2.cfg:
[unittest]
plugins = nose2.plugins.layers
nose2.plugins.attrib
nose2.plugins.junitxml
nose2.plugins.coverage
start-dir = tests
test-file-pattern = test_*.py
[junit-xml]
always-on = True
path = build/junit.xml
[coverage]
always-on = True
coverage = src
coverage-report = term-missingStep 4 - Layers (nose2-distinctive)
Layers are setUp/tearDown shared across multiple test classes:
class DatabaseLayer(object):
@classmethod
def setUp(cls):
cls.db = create_test_db()
@classmethod
def tearDown(cls):
cls.db.close()
class TestUsers(unittest.TestCase):
layer = DatabaseLayer
def test_create_user(self):
user = create_user(self.layer.db)
self.assertEqual(user.id, 1)
class TestOrders(unittest.TestCase):
layer = DatabaseLayer # shares the same DB connection
def test_create_order(self):
...Layers establish setup once; multiple test classes consume. Pytest's session-scoped fixtures cover similar territory.
Step 5 - Parameterized tests
from nose2.tools import params
@params(
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
)
def test_sum_param(a, b, expected):
assert sum(a, b) == expectedEach row runs as a separate test - failures don't stop subsequent rows.
Step 6 - Attribute-based filtering
from nose2.tools import attr
class TestSlow(unittest.TestCase):
@attr('slow', type='integration')
def test_slow_thing(self):
...Run only marked tests:
nose2 -A 'slow'
nose2 -A 'type=integration'Equivalent to pytest markers (cleaner in pytest).
Step 7 - Plugins
| Plugin | Use |
|---|---|
nose2.plugins.layers | Layer support (Step 4) |
nose2.plugins.attrib | Attribute-based filtering (Step 6) |
nose2.plugins.junitxml | JUnit XML output for CI dashboards |
nose2.plugins.coverage | Coverage integration |
nose2.plugins.mp | Multi-process parallel execution |
nose2.plugins.printhooks | Debug hook visualization |
Enable via unittest.cfg plugins = list (Step 3).
Step 8 - Migration from nose1
nose1's API:
| nose1 | nose2 equivalent |
|---|---|
nosetests command | nose2 command |
nose.tools.eq_ / ok_ / raises | unittest self.assertEqual / etc., or pytest-style assert |
from nose.tools import with_setup | TestCase setUp/tearDown |
nose.SkipTest | unittest.SkipTest |
@attr('slow') | @attr('slow') (same in nose2 with attrib plugin) |
Most test bodies survive migration unchanged; mostly config + import path changes.
For more aggressive migration, consider migrating directly to pytest - discovery is even simpler + ecosystem is much richer.
Step 9 - CI integration
- run: pip install nose2
- run: nose2 -v --plugin=nose2.plugins.junitxml --junit-xml=junit.xmlFor coverage:
coverage run -m nose2
coverage report --fail-under=80Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Start new project with nose2 | Pytest is the mainstream; nose2 community smaller | Use pytest-tests |
Use nose1 (nose package) | Unmaintained since 2015 | Migrate to nose2 (Step 8) or pytest |
| Layers for stateful resources | State leaks across test classes | Per-class setUp or pytest function-scope fixtures |
| Skip plugin enable in config | Features (layers, attrib, etc.) unavailable | Enable in unittest.cfg (Step 3) |
Use nosetests command (nose1) | Wrong runner | Use nose2 (Step 2) |
Limitations
References
Related skills
doctest-tests
Configures and runs Python's stdlib doctest - embeds executable test cases in docstrings using `>>>` Python interactive prompt convention; supports `# doctest: +ELLIPSIS` / `+NORMALIZE_WHITESPACE` / `+SKIP` directives; integrates with pytest via `--doctest-modules` flag; runs as `python -m doctest module.py -v`. Use for self-documenting reference implementations + simple smoke-test coverage embedded in API docs.
pytest-asyncio-patterns
Configures and runs async Python tests with pytest-asyncio: installs the plugin, selects asyncio_mode (auto vs strict), scopes event loops (function/class/module/session), writes async fixtures with @pytest_asyncio.fixture, mocks coroutines with AsyncMock, and tests FastAPI (httpx.AsyncClient + ASGITransport) and aiohttp (aiohttp_client fixture) applications. Use when a Python project contains async def test_ functions, FastAPI/aiohttp endpoints, or any asyncio-based code that needs pytest integration. Do NOT use for general pytest fixture design, parametrize patterns, or conftest.py structure without an asyncio-specific problem (event-loop scoping, mode config, AsyncMock, ASGI client): use pytest-tests for those.
pytest-tests
Configures and runs pytest - the de facto Python test framework with fixture-based dependency injection (`@pytest.fixture` with scopes module/session/function), parametrize for table-driven tests (`@pytest.mark.parametrize`), markers (`@pytest.mark.skip` / `xfail` / `slow`), `conftest.py` for shared fixtures, plugin ecosystem (pytest-cov, pytest-asyncio, pytest-mock, pytest-xdist), `--lf`/`--ff` for fail-loop, coverage gating. Use when working with Python and needing the modern test framework.
unittest-tests
Configures and runs Python's stdlib unittest - TestCase + setUp/tearDown lifecycle hooks, assertion catalog (assertEqual / assertRaises / assertIn / assertAlmostEqual), unittest.mock module (Mock / MagicMock / patch / patch.object / patch.dict), test discovery via `python -m unittest discover`, subTest for parametrized cases, expectedFailure decorator. Use when constrained to stdlib-only (no pip install) or migrating legacy unittest codebases.