Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Write Unit Tests with unittest

Learn to write unit tests using Python's unittest module. This hands-on lesson covers core concepts, step-by-step walkthroughs, and troubleshooting for reliable code testing.

Focus: write unit tests with unittest module

Sponsored

You've written Python functions, built classes, and created packages—but how do you know your code works correctly after every change? Without automated tests, you're one refactor away from a silent bug. This lesson teaches you to write unit tests with Python's built-in unittest module, transforming vague confidence into repeatable, machine-verified proof.

The problem this lesson solves

Manual testing is slow, error-prone, and impossible to scale. When you change one function, you can't manually re-run every scenario in a 500-line module. Unit tests capture those scenarios as code. They give you an immediate red or green signal so you can refactor fearlessly and catch regressions the moment they appear.

Core concept / mental model

Think of a unit test as a safety net. Each test is a tiny program that: 1. Sets up a known starting state 2. Calls the function or method you want to check 3. Asserts that the result matches your expectation

unittest is Python's standard testing framework. It provides TestCase as the base class, and you write test methods inside that class. Each test method enforces one **assertion**—a claim about your code's behavior.

Pro tip: A good unit test tests one behavior, not one function. A function with three conditional branches may need three tests.

How it works step by step

Step 1: Create a test file

Name your file with a test_ prefix (e.g., test_calculator.py). This is a naming convention that unittest's test runner recognizes.

Step 2: Import unittest and your code

import unittest
from calculator import add, divide   # your module

Step 3: Define a test class that inherits from unittest.TestCase

class TestCalculator(unittest.TestCase):
    def test_add_positive_numbers(self):
        result = add(2, 3)
        self.assertEqual(result, 5)

Step 4: Use assertion methods

Common assertions include: - assertEqual(a, b) — checks a == b - assertTrue(x) — checks x is True - assertFalse(x) — checks x is False - assertRaises(SomeException, callable, arg) — checks that an exception is raised

Step 5: Run the test

From the terminal:

python -m unittest test_calculator.py

You'll see a dot (.) for each passing test, an F for failure, or an E for error.

Hands-on walkthrough

Let's build a full example. Write a module calc.py:

# calc.py
def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

def is_even(n):
    return n % 2 == 0

Now create test_calc.py:

import unittest
from calc import add, divide, is_even

class TestCalc(unittest.TestCase):

    def setUp(self):
        """Runs before every test method."""
        self.numbers = [1, 2, 3]

    def test_add_integers(self):
        self.assertEqual(add(2, 3), 5)

    def test_add_floats(self):
        self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=5)

    def test_divide_positive(self):
        self.assertEqual(divide(10, 2), 5.0)

    def test_divide_by_zero(self):
        with self.assertRaises(ValueError):
            divide(5, 0)

    def test_is_even_true(self):
        self.assertTrue(is_even(4))

    def test_is_even_false(self):
        self.assertFalse(is_even(3))

if __name__ == '__main__':
    unittest.main()

Run it:

$ python -m unittest test_calc.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.001s

OK

Six dots = six passing tests. If you change add to return a + b + 1, the next run would show F..... and explain the failure.

Compare options / when to choose what

Tool Use Case Best For
unittest Built-in, no extra install Team standards, CI, large projects
pytest Third-party, simpler syntax Rapid TDD, small scripts, less boilerplate
doctest Tests inside docstrings Quick acceptance checks, documentation examples

Pro tip: Start with unittest if you want a zero-dependency solution. Switch to pytest if you need powerful fixtures or parameterization later.

Troubleshooting & edge cases

Test method names must start with test_

If you name a method add_test, unittest will silently ignore it. Always prefix with test_.

Floating-point comparison

assertEqual(0.1 + 0.2, 0.3) will fail due to rounding. Use assertAlmostEqual or round:

self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=5)

Testing exceptions with context manager

Use with self.assertRaises(...) to ensure an exception is raised within that block. If the exception isn't raised, the test fails.

setUp and tearDown

Define setUp() to create common test data once per test. It runs before every test method. Similarly, tearDown() runs after each test to clean up resources (e.g., close file handles).

Test discovery

Run all tests in a directory:

python -m unittest discover -s tests -p "test_*.py"

This finds all files matching test_*.py in the tests/ folder.

What you learned & what's next

You now know how to write unit tests with the unittest module: creating TestCase subclasses, using assertion methods, running tests from the command line, and handling common edge cases like floating-point precision and exception testing. This skill lets you verify your code automatically and refactor with confidence.

Next, you'll explore test coverage—measuring how much of your code is actually exercised by your tests. Or, if you're ready to dive deeper into Python's testing ecosystem, look into unittest.mock for replacing dependencies during testing.

Keep testing everything—your future self will thank you.

Practice recap

Now write a test suite for your own small project. Pick a module with at least three functions (one that raises an exception on invalid input, one that returns a boolean, and one that computes a numeric result). Create test_<module>.py with six test methods covering normal cases and edge cases. Run the tests and fix any failures. Then add a setUp() method to share test data across multiple tests.

Common mistakes

  • Forgetting to prefix test method names with test_unittest silently skips methods that don't start with test_.
  • Using assertEqual for floating-point numbers — 0.1 + 0.2 != 0.3 due to binary representation; use assertAlmostEqual instead.
  • Calling unittest.main() inside the wrong if block — always guard with if __name__ == '__main__' to prevent running tests on import.
  • Writing tests that depend on test execution order — each test should be independent. Shared state in class attributes can cause intermittent failures.

Variations

  1. Use pytest for simpler syntax: def test_add(): assert add(2,3) == 5 — no class required.
  2. Use doctest to embed tests directly in docstrings: >>> add(2,3) 5 — ideal for simple examples in documentation.
  3. Use hypothesis library for property-based testing: automatically generates many inputs to find edge cases.

Real-world use cases

  • A CI pipeline runs python -m unittest discover on every pull request to catch regressions before merging.
  • An API client module uses unittest.mock to simulate HTTP responses, isolating network calls from core logic tests.
  • A financial calculation library uses assertAlmostEqual with places=2 to verify currency computations without floating-point drift.

Key takeaways

  • Unit tests automate verification of individual functions or methods, catching regressions immediately.
  • Use unittest.TestCase as a base class and write test methods prefixed with test_.
  • Choose the right assertion: assertEqual, assertTrue, assertRaises, and assertAlmostEqual for floats.
  • Run tests with python -m unittest <file> or use discover for entire test directories.
  • Always name test files with a test_ prefix to enable automatic discovery.
  • Independent, isolated tests lead to reliable suites — avoid sharing state across tests.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.