Your first test¶
Welcome to your first tutorial on unit testing with Python's built-in unittest module! In this tutorial, you'll write your very first test and see it run successfully.
Time commitment: 15–20 minutes
Before you start¶
This tutorial assumes you:
- Can run Python code in Jupyter notebooks or Google Colab
- Understand basic Python syntax (variables, functions, return statements)
- Have written at least a few simple Python functions before
If you're completely new to Python, we recommend completing a basic Python introduction first.
If some concepts seem unfamiliar, don't worry - you'll pick them up as we go. Focus on following the patterns shown in the examples.
Learning objectives¶
By the end of this tutorial, you will be able to:
- Understand what unit testing is and why it matters
- Write a simple function and a test for it
- Use the
assertEqual()assertion - Run tests using Python's unittest framework
- Interpret test output (successes and failures)
What is unit testing?¶
Unit testing is a software development practice where you write small, automated tests to verify that individual "units" of code (typically functions or methods) work correctly. Think of it as a safety net that catches bugs before they reach production.
Why should you test?¶
- Confidence: Know your code works as expected
- Documentation: Tests describe how your code should behave
- Safety: Change code without breaking existing functionality
- Early bug detection: Catch problems before users do
Unit testing in professional software development¶
In professional settings, tests help you:
- Verify your code works correctly before deployment
- Catch bugs early when they're cheap to fix
- Make changes confidently without breaking existing functionality
- Document how your code should behave for other developers
In 1996, the Ariane 5 rocket exploded 37 seconds after launch due to a software error that could have been caught by tests. The project cost £6 billion.
Good tests are like insurance - you hope you never need them, but you're grateful when you do.
Now let's write our first test!
Step 1: Writing a simple function¶
Before we can test anything, we need some code to test. Let's start with a simple function that adds two numbers:
def add(a, b):
"""Add two numbers and return the result."""
return a + b
Let's try it out to make sure it works:
# Try the function
result = add(2, 3)
print(f"2 + 3 = {result}")
Great! But what if we change the function later? How do we know we didn't break it? This is where tests come in.
Step 2: Writing your first test¶
To write a test with unittest, we need to:
- Import the
unittestmodule - Create a test class that inherits from
unittest.TestCase - Write test methods (their names must start with
test_) - Use assertions to check expected behaviour
Let's create a test for our add() function:
import unittest
class TestAddFunction(unittest.TestCase):
"""Tests for the add() function."""
def test_add_positive_numbers(self):
"""Test that adding two positive numbers works correctly."""
result = add(2, 3)
self.assertEqual(result, 5)
Understanding the test structure¶
Don't worry if this looks complex! You don't need to fully understand Python classes yet to write tests. For now, treat this as a recipe you follow:
import unittest- brings in the testing toolsclass TestAddFunction(unittest.TestCase):- creates a container for your testsdef test_add_positive_numbers(self):- defines a test (must start withtest_)self.assertEqual(...)- checks if something is correct
Think of self as a way to say "use the testing tools".
Want to understand what self, classes, and TestCase really mean? See Understanding Test Structure for a deeper explanation. For now, focus on the pattern.
Step 3: Running the test¶
Running tests in Jupyter notebooks¶
In Jupyter, we need special arguments to run unittest:
unittest.main(argv=[''], verbosity=2, exit=False)
What do these arguments mean?
argv=['']: Prevents Jupyter from interfering with command-line argumentsverbosity=2: Shows detailed test outputexit=False: Prevents the notebook from shutting down after tests run
For more details on running tests in Jupyter, including troubleshooting tips, see How to Run Tests in Jupyter.
Let's run our test:
# Run the test (in Jupyter, we need these special arguments)
unittest.main(argv=[''], verbosity=2, exit=False)
Understanding the output¶
When you run the test, you'll see output like:
test_add_positive_numbers (__main__.TestAddFunction)
Test that adding two positive numbers works correctly. ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
- The test name and docstring are displayed
- "ok" means the test passed
- We can see how many tests ran and how long they took
- "OK" at the end means all tests passed
Step 4: What happens when a test fails?¶
Let's deliberately write a failing test to see what happens. We'll test a buggy function:
def buggy_add(a, b):
"""A buggy version of add that always adds one extra."""
return a + b + 1 # Bug: adds an extra 1!
class TestBuggyAdd(unittest.TestCase):
"""This test will fail!"""
def test_buggy_add(self):
"""This test demonstrates a failure."""
result = buggy_add(2, 3)
self.assertEqual(result, 5) # We expect 5, but we'll get 6!
# Run the failing test
unittest.main(argv=[''], verbosity=2, exit=False)
Understanding failure output¶
When a test fails, you'll see:
FAIL: test_buggy_add (__main__.TestBuggyAdd)
This test demonstrates a failure.
----------------------------------------------------------------------
Traceback (most recent call last):
...
AssertionError: 6 != 5
- "FAIL" indicates a failed assertion
- The traceback shows where the failure occurred
AssertionError: 6 != 5tells you what went wrong
This is incredibly useful for debugging!
Quick reference¶
Here are the essentials to remember:
Basic test structure¶
import unittest
class TestMyFunction(unittest.TestCase):
def test_specific_scenario(self):
"""Clear description of what this test checks."""
result = my_function(input_value)
self.assertEqual(result, expected_output)
# For Jupyter notebooks
unittest.main(argv=[''], verbosity=2, exit=False)
Golden rules¶
- Test names must start with
test_ - Use
self.before assertions - One test, one behaviour
Key takeaways¶
Congratulations! You've written and run your first test. Let's recap:
- Tests are classes that inherit from
unittest.TestCase - Test methods must start with
test_so unittest can find them - Assertions (like
assertEqual()) check expected behaviour - Tests can pass or fail - failures help you find bugs
- Use
unittest.main(argv=[''], verbosity=2, exit=False)to run tests in Jupyter
Next steps¶
Now that you've written your first test, continue to the next tutorial:
- Testing Thoroughly - Learn to test multiple scenarios, understand edge cases, and build comprehensive test suites
Supporting resources¶
- Understanding Test Structure - Deep dive into classes,
self, andTestCase - How to Run Tests in Jupyter - Complete guide with troubleshooting
Remember: Testing is a skill that improves with practice. Every test you write makes you a better developer.
Happy testing!