Testing is the practice of writing code that checks whether your other code works correctly. More than that — tests give you the confidence to *change* code. When you refactor, fix a bug, or add a feature, the test suite immediately tells you if something broke elsewhere. Without tests, every change carries silent risk.
The most basic test has three steps:
1. Call your production function with a specific input
2. Capture the result
3. Assert the result equals what you expected
If the assertion is true, the test passes. If not, pytest shows you exactly what differed.
The `assert` keyword is Python's built-in mechanism for this: it evaluates an expression and raises `AssertionError` if the result is falsy. In a test you write `assert result == expected` — that's the whole foundation. The next topic (*Assertions in Depth*) explains how pytest dramatically improves `assert` output and covers patterns for comparing floats, lists, dicts, and more.
**Why pytest, not unittest?**
Python's standard library includes `unittest`, but it's verbose. You must inherit from `TestCase`, use `self.assertEqual()` instead of plain `assert`, and wrap everything in classes. pytest needs none of that:
```python
# unittest — a lot of ceremony
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
# pytest — just a function
def test_add():
assert add(2, 3) == 5
```
Beyond syntax, pytest's failure messages are far more informative — it shows the actual values that were compared. And pytest's fixtures, parametrize, and plugin ecosystem are vastly better.
**Installation:**
```bash
pip install pytest
```
**How test discovery works:**
pytest walks your project directory and collects tests by following these naming rules:
- Files: `test_*.py` or `*_test.py`
- Functions at module level: start with `test_`
- Classes: start with `Test` (no `__init__` method)
- Methods inside `Test*` classes: start with `test_`
Everything else is ignored. `helper.py`, `utils.py`, `main.py` — not collected. A function named `check_result()` — not collected. Only the pattern `test_*` triggers collection.
**Running tests:**
```bash
pytest # run all discovered tests
pytest -v # verbose: show each test name and result
pytest -s # show print() output (don't suppress stdout)
pytest --tb=short # shorter tracebacks on failure
pytest --collect-only # show what would run without running it
```
```bash
pytest # run all tests
pytest -v # verbose: show each test name
pytest -s # show print() output (don't capture stdout)
pytest test_foo.py # run one file
pytest -k "add" # run tests whose name contains "add"
pytest --tb=short # shorter tracebacks
pytest --tb=no # no tracebacks (just pass/fail count)
```
**Naming rules pytest follows:**
- Files: `test_*.py` or `*_test.py`
- Functions and methods: `test_*`
- Classes: `Test*` (no `__init__` method)
**What pytest outputs:**
```
collected 4 items
test_math_utils.py::test_add_positive_numbers PASSED
test_math_utils.py::test_add_negative PASSED
test_math_utils.py::test_clamp_within_range PASSED
test_math_utils.py::test_clamp_below_lower PASSED
4 passed in 0.05s
```
**When to add `import pytest`:**
Simple tests need only `assert` and your own imports — no `import pytest`. You add it when using pytest-specific features: `pytest.approx` (float comparisons), `pytest.raises` (exception testing), `@pytest.fixture`, or marks like `@pytest.mark.parametrize`. These are introduced in the following topics.
Write test functions for `is_even(n)` that verify it returns `True` for even numbers and `False` for odd numbers. Write at least two test functions — one for even inputs, one for odd.
The test below contains a deliberate bug — the expected value is wrong. Run pytest and read the failure output, then fix the expected value to make the test pass.
def multiply(a, b):
return a * b
def test_multiply():
assert multiply(3, 4) == 11 # wrong expected value
assert multiply(0, 99) == 0
assert multiply(-2, 5) == -10
Write three separate test functions for `clamp(value, lo, hi)`: one for values within range, one for values below the lower bound, and one for values above the upper bound.
Write tests for `greet(name)` that returns `'Hello, {name}!'`. Verify that the return value starts with 'Hello', ends with '!', and contains the provided name.
def greet(name):
return f'Hello, {name}!'
def test_greet_alice():
result = greet('Alice')
assert result == 'Hello, Alice!'
assert result.startswith('Hello')
assert result.endswith('!')
assert 'Alice' in result
def test_greet_empty_name():
result = greet('')
assert result == 'Hello, !'
No split tab
Cookie preferences
We use necessary cookies to run the site. With your permission, we can also save your site preferences and use analytics and advertising cookies to understand usage and support the project.
Open tools in tabs.Exercises, IDE tools, and trainers stay available as site tabs.
Switch without losing context.Move between explanations, code, and utilities while keeping your place.
Use the sidebar as your map.The left panels hold navigation, settings, files, libraries, and tool controls.
PythonJavaScriptSQLite
One IDE, three practical modes
Python in the browser.Run small scripts, try libraries, and practice API requests without installing anything.
JavaScript for quick experiments.Test browser-friendly code and compare ideas next to your learning materials.
SQLite for data practice.Open the database explorer to inspect tables, write queries, and learn SQL workflows locally.
TopicIDE
Work side by side with split tabs
Keep instructions visible.Open an exercise or reference page beside the IDE instead of jumping back and forth.
Compare tools while you learn.Place regex checks, explanations, and code experiments next to each other when the task needs it.
Close the split when you are done.The workspace returns to a single focused tab, and your open site tabs remain available.
Code Typing Trainer
Or plain text
This trainer is designed for a physical keyboard.Open this section on a laptop or desktop with a wide screen. Touch typing practice will not work correctly on a phone.
Speed: 0 chars/min
0 words/min
Best speed (60s): 0 chars/min
0 words/min
Errors: 0
Total time: 0.0 s
To practice touch typing, avoid looking at your physical keyboard.