Python's unittest module is a full testing framework included in every Python installation. You do not need to install it, configure it, or choose between competing options. When you write import unittest, you have access to test cases, test suites, test runners, and a command-line interface that discovers and executes your tests.
This article builds on the test you wrote in write your first Python unit test and covers the features you will use every day: the assertion methods, the setUp and tearDown methods for preparing test data, command-line options, and the skip and subtest utilities.
The assertion methods you will use most
Every test ends with an assertion, a statement that declares what must be true for the test to pass. The unittest.TestCase class provides a family of assertion methods that are more informative than the built-in assert statement. When an assertion fails, these methods print both the expected value and the actual value.
The assertEqual method is the workhorse. It checks that two values are equal and works with strings, numbers, lists, dictionaries, and most built-in types.
import unittest
class TestAssertEqual(unittest.TestCase):
def test_string_equality(self):
self.assertEqual("hello", "hello")
def test_list_equality(self):
self.assertEqual([1, 2, 3], [1, 2, 3])The assertTrue and assertFalse methods check boolean conditions. Use them when the function you are testing returns a boolean or when you need to verify a condition.
def test_value_in_range(self):
age = 25
self.assertTrue(18 <= age <= 65)
def test_list_is_not_empty(self):
items = ["apple", "banana"]
self.assertFalse(len(items) == 0)The assertIn method checks membership in a container. It produces clearer failure messages than writing self.assertTrue(value in collection) because it prints both the value and the collection when the assertion fails.
def test_item_in_list(self):
fruits = ["apple", "banana", "cherry"]
self.assertIn("banana", fruits)The assertRaises method verifies that a specific exception is raised. This matters when you test error handling: a function should raise a ValueError when given invalid input.
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
result = 10 / 0The context manager form is the most readable pattern. The test passes only if the exact exception type is raised inside the block.
setUp and tearDown for shared test fixtures
Most test classes share setup code across multiple methods. Instead of creating the same data at the start of every method, define a setUp method that runs before each test. The framework calls setUp, then the test method, then tearDown, and repeats this for every test.
class TestShoppingCart(unittest.TestCase):
def setUp(self):
self.cart = []
self.item = {"name": "book", "price": 12.99}
def test_add_item(self):
self.cart.append(self.item)
self.assertEqual(len(self.cart), 1)
def test_empty_cart_total(self):
self.assertEqual(len(self.cart), 0)The setUp method creates a fresh empty list and a sample item before every test. Because setUp runs before each test independently, you never have to worry about one test's changes leaking into another. The test_empty_cart_total test can safely assume the cart starts empty, even if test_add_item ran before it.
For expensive resources like database connections, use setUpClass instead. This class method runs once for the entire test class rather than once per test method.
class TestWithDatabase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.connection = create_test_database()
@classmethod
def tearDownClass(cls):
cls.connection.close()The @classmethod decorator is required for setUpClass and tearDownClass. The connection is created once and shared across all test methods, saving time when setup is expensive.
Running tests from the command line
The unittest module provides a command-line interface with several useful options. The basic command runs all tests in a module:
python -m unittest test_shopping_cart.pyRun with the -v flag for verbose output that shows each test name on its own line. You can also narrow the run to a specific class or a single method:
python -m unittest test_shopping_cart.TestShoppingCart
python -m unittest test_shopping_cart.TestShoppingCart.test_add_itemThe -k flag filters tests by name pattern, so you can run only the tests whose names match a given keyword. This is helpful when you are working on one feature and want fast feedback without waiting for unrelated tests to finish. For example, to run everything related to cart operations without touching other test classes, use this command:
python -m unittest -k "cart"Running python -m unittest without arguments triggers test discovery, which searches the current directory and subdirectories for files matching test*.py and runs every test it finds.
Skipping tests and expected failures
Not every test is ready to run at every moment. The unittest module provides decorators for these situations.
The skip decorator unconditionally skips a test:
@unittest.skip("Payment integration not yet implemented")
def test_process_payment(self):
result = process_payment(amount=100)
self.assertTrue(result.success)The skip decorator is useful when you know in advance that a test cannot pass yet, such as a feature that is still under construction. The skipIf decorator skips a test only when a condition is true, which matters when a test depends on the operating system, a Python version, or an optional dependency:
import sys
@unittest.skipIf(sys.platform != "linux", "Requires Linux")
def test_symlink_handling(self):
result = resolve_symlinks("/path/to/link")
self.assertIsNotNone(result)The expectedFailure decorator marks a test known to fail. The test runs, but a failure is reported as expected rather than a regression:
@unittest.expectedFailure
def test_legacy_format(self):
self.assertEqual(format_date_v1("2026-07-13"), "July 13, 2026")This decorator is useful when you discover a bug and write a test for it before fixing the underlying code. The test documents the expected fix without breaking your CI pipeline.
Subtests for multiple inputs
When you have several similar inputs to test, writing a separate method for each one creates repetition. The subTest context manager lets you loop over inputs inside a single method while reporting each iteration independently.
class TestNumberClassification(unittest.TestCase):
def test_even_numbers(self):
for number in [2, 4, 6, 8, 10]:
with self.subTest(number=number):
self.assertEqual(number % 2, 0)Without subTest, the loop stops at the first failure. With subTest, every iteration runs and every failure is reported independently. The output shows the value of number for each failed iteration, making it easy to identify which input caused the problem.
The subTest call accepts keyword arguments that appear in the failure output. Use descriptive names like input_value or scenario so the failure message tells you exactly which case broke.
Once you understand the unittest module, the next step is to explore pytest, which offers a simpler syntax and a powerful fixture system.
Rune AI
Key Insights
- unittest.TestCase provides assertion methods like assertEqual, assertTrue, assertRaises, and assertIn that report failures without stopping the test run.
- setUp and tearDown create fresh test fixtures before and after each test method, ensuring tests do not interfere with each other.
- python -m unittest discovers and runs all tests in a project automatically when they follow the naming conventions.
- Use @unittest.skip and @unittest.expectedFailure to handle tests that are not ready to run or represent known bugs.
- subTest lets you loop over multiple inputs within a single test method while reporting each iteration's result independently.
Frequently Asked Questions
Why does unittest use assertEqual instead of the built-in assert statement?
What is the difference between setUp and setUpClass?
How do I run only a specific test instead of the whole file?
Conclusion
unittest is a complete testing framework that ships with Python and covers nearly every testing need without third-party dependencies. Its assertion methods, fixture system, test discovery, and skip support give you the tools to build a reliable test suite for any project size.
More in this topic
How to Test Python Code
Learn what testing means in Python, the core ideas behind automated tests, and the built-in and third-party tools that make testing practical.
Write Your First Python Unit Test
Write and run your first Python unit test step by step, using unittest to check a small function and understand the test output.
The Factory Pattern in Python
Learn how to use the factory pattern in Python to encapsulate object creation, making your code more flexible and easier to extend.