unittest β Standard Library
import unittest
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
class TestMath(unittest.TestCase):
def test_divide_normal(self):
self.assertAlmostEqual(divide(10, 3), 3.333, places=3)
def test_divide_zero(self):
with self.assertRaises(ValueError):
divide(10, 0)
if __name__ == "__main__":
unittest.main()pytest β Industry Standard
import pytest
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(-1, 1, 0),
(0, 0, 0),
])
def test_add_parametrize(a, b, expected):
assert add(a, b) == expectedFixtures
import pytest
import sqlite3
@pytest.fixture
def db():
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
yield conn
conn.close()
def test_insert(db):
db.execute("INSERT INTO users VALUES (1, 'Alice')")
result = db.execute("SELECT name FROM users").fetchone()
assert result[0] == "Alice" Mocking
from unittest.mock import patch, MagicMock
import requests
def fetch_user(user_id):
resp = requests.get(f"https://api.example.com/users/{user_id}")
return resp.json()
def test_fetch_user():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"id": 1, "name": "Bob"}
user = fetch_user(1)
assert user["name"] == "Bob" pytest -v --cov=mymodule # pip install pytest-covTesting with pytest: Confidence to Change Code
Tests are code that checks your code. Their real payoff isn't catching today's bug β it's letting you refactor fearlessly later, because a green test suite proves you didn't break anything. pytest makes them nearly frictionless: plain functions and plain assert.
# test_math.py β pytest auto-discovers test_* functions
def add(a, b): return a + b
def test_add():
# Arrange β Act β Assert
result = add(2, 3) # Act
assert result == 5 # Assert
import pytest
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError): # assert it raises
1 / 0
| Concept | What it is |
|---|---|
| AAA pattern | Arrange, Act, Assert β structure of a good test |
| Fixture | @pytest.fixture β reusable setup (db, sample data) |
| Mock | fake a slow/external dependency (API, clock) |
| Parametrize | run one test over many inputs |
What to test: behavior, not implementation β assert on outputs, not internal steps, so tests survive refactors. Cover the happy path, edge cases (empty, zero, negative), and error cases. Mock external services so tests stay fast and deterministic. Run them on every commit (CI) and one broken test blocks the merge.
ποΈ Practical Exercise
Write your first tests:
- Write a simple function and a
unittest.TestCasethat asserts its output. - Rewrite the same test as a plain
pytestfunction usingassert. - Create a pytest
fixturethat provides sample data to multiple tests. - Mock an external call (e.g. a network request) so the test runs offline.
π₯ Challenge Exercise
Add a test suite to a small module (e.g. a calculator or a validation function): cover normal cases, edge cases, and error cases (using pytest.raises). Use a fixture for shared setup and parametrize a test to run across many inputs with @pytest.mark.parametrize. Bonus: mock a function that talks to an external service and verify your code calls it correctly without hitting the network.
π Summary
- Automated tests catch regressions and let you refactor with confidence.
unittestis the class-based standard library framework;pytestis the popular, terser third-party choice.- Unit tests check small pieces in isolation; integration tests check components working together.
- Fixtures provide reusable setup/teardown and test data.
- Mocking replaces external dependencies so tests stay fast and deterministic.
- Parametrization runs the same test across many inputs; coverage measures tested code but is not a goal in itself.
Interview Questions on Testing
- Why is automated testing important?
- What is the difference between
unittestandpytest? - What is a unit test versus an integration test?
- What is a fixture and why is it useful?
- What is mocking and when do you use it?
- What does test parametrization achieve?
- What is test coverage and is 100% coverage the goal?
Related Topics
FAQ
unittest or pytest? +pytest is the most popular choice: it uses plain assert statements, has powerful fixtures and parametrization, and less boilerplate. unittest ships with Python and is fine too, especially in codebases already using it. pytest can even run unittest tests.
Mocking replaces a real dependency (a network call, database, or clock) with a controllable stand-in during a test. This keeps tests fast, deterministic, and isolated, and lets you assert how your code interacts with that dependency.
A fixture is reusable setup code that provides data or resources to tests β like a sample object, a temporary database, or a configured client. In pytest you declare it with @pytest.fixture and request it by adding its name as a test argument.
No. Coverage shows which lines ran during tests, but high coverage with weak assertions proves little. Aim to test important behavior, edge cases, and failure paths; meaningful tests matter more than a coverage percentage.
