How do I run unittest tests inside a Jupyter notebook?¶
Notebooks don't have the command-line test runner that python -m unittest gives you, so the usual instructions for running a TestCase don't quite work as written. The trick is to call unittest.main() with two specific arguments — argv to stop it choking on the notebook's argv, and exit=False to stop it killing the kernel.
This recipe gives you the exact one-line invocation to drop under your test class, plus the variants for verbose output and running just one test.
import unittest
class TestArithmetic(unittest.TestCase):
def test_addition(self):
self.assertEqual(2 + 2, 4)
def test_subtraction(self):
self.assertEqual(5 - 3, 2)
# This is the line that makes unittest work in a notebook.
# - argv=[''] stops unittest from trying to parse the kernel's argv
# - exit=False stops it from calling sys.exit() and killing the kernel
unittest.main(argv=[''], exit=False, verbosity=2)
Two variations: running a single test by name, and the difference between verbosity levels.
import unittest
class TestNames(unittest.TestCase):
def test_alpha(self):
self.assertTrue("a".isalpha())
def test_digit(self):
self.assertTrue("1".isdigit())
def test_space(self):
self.assertTrue(" ".isspace())
# Run just one test by passing its dotted name in argv.
# The format is __main__.<TestClass>.<test_method>
unittest.main(
argv=['', '__main__.TestNames.test_digit'],
exit=False,
verbosity=2,
)
import unittest
class TestVerbosity(unittest.TestCase):
def test_one(self):
self.assertEqual(1, 1)
def test_two(self):
self.assertEqual(2, 2)
print("=== verbosity=0 (quiet) ===")
unittest.main(argv=[''], exit=False, verbosity=0)
print("\n=== verbosity=1 (default — one dot per test) ===")
unittest.main(argv=[''], exit=False, verbosity=1)
print("\n=== verbosity=2 (one line per test, with method name) ===")
unittest.main(argv=[''], exit=False, verbosity=2)
Why it works¶
unittest.main() is built for command-line use. By default it does two things that are useful from the shell and disastrous in a notebook.
First, it reads sys.argv to figure out which tests to run. In a script that gives you python my_tests.py TestSomething.test_one. In a Jupyter kernel, sys.argv is a Jupyter-specific list — usually starts with '/path/to/ipykernel_launcher.py' and is followed by connection-file arguments — and unittest.main will try to interpret those as test names, then complain when they aren't. Passing argv=[''] (a list containing one empty string, mimicking "no command-line args") sidesteps the whole mess.
Second, it calls sys.exit() when finished, with the exit code reflecting whether tests passed. From the shell that's exactly right — your CI script needs the exit code. In a notebook, sys.exit() raises SystemExit, which terminates the kernel cell and (depending on the front-end) can leave the kernel in an awkward state. exit=False makes unittest.main return the test result instead of exiting.
verbosity=2 is worth the extra characters. The default verbosity=1 prints one dot per test, which is fine for hundreds of tests but uninformative for the handful you typically run in a notebook. verbosity=2 prints the test method name and PASS/FAIL/ERROR for each one, so you can see what actually ran without having to re-read the cell.
Trade-offs¶
unittest.main() discovers tests in the current module. In a notebook, "the current module" is __main__, which means it finds every TestCase subclass defined in any cell that's been run so far. That's usually what you want when you're iterating — re-define a class, re-run, see results — but it bites if you've left old test classes lying around in earlier cells. If you want to run only one class, use unittest.TestLoader().loadTestsFromTestCase(MyTests) and a TextTestRunner instead.
pytest runs in notebooks too, via ipytest or just by writing tests in a .py file and shelling out. If your project already uses pytest for its real test suite, prefer that over re-learning unittest semantics for a notebook. Use this recipe when you want to demonstrate a unittest-based pattern without setting up an external runner.
The notebook is not a substitute for a proper test suite. It's great for exploration — try a pattern, see what fails, iterate — but the tests that actually gate your code should live in a tests/ folder and run from CI. Treat the notebook as a sketchbook.
Related¶
- How to test exceptions and error handling —
assertRaisespatterns, also runnable from a notebook. - How to avoid common testing mistakes — the traps that catch people writing their first tests.
unittestquick reference — assertion methods, fixtures, and lifecycle hooks at a glance.- Test naming conventions — why every test method needs the
test_prefix.