How to Use Pytest

What you’ll build or solve

You’ll write and run tests with Pytest so you can catch bugs early and refactor with more confidence.

When this approach works best

Pytest works best when you want to:

  • Add fast unit tests for functions, like parsing input or calculating values.
  • Prevent regressions after refactoring, so old behavior stays consistent.
  • Check edge cases, like empty input, invalid data, or boundary values.

This is a bad idea when you are writing a throwaway script you will never run again. For anything you plan to maintain, tests pay off.

Prerequisites

  • Python 3 installed
  • Pytest installed in the environment you use for the project

If you need to install it:

python -m pip install pytest

Step-by-step instructions

1) Create test files Pytest can discover

Pytest looks for files named test_*.py or *_test.py. Put your tests in one of those files and name test functions with test_.

# test_math_utils.py

def add(a, b):
    return a + b

def test_adds_two_numbers():
    assert add(2, 3) == 5

What to look for: If your file or function name does not match these patterns, Pytest will skip it.


2) Run your tests from the command line

From the folder that contains your test file, run:

pytest

For a more detailed list of tests:

pytest -v

What to look for: If Pytest says “collected 0 items,” your test file or test function names do not match discovery rules.


3) Write assertions that check behavior

Pytest uses normal Python assert statements. Keep them focused on one behavior at a time.

def format_username(name):
    return name.strip().lower().replace(" ", "_")

def test_formats_username():
    assert format_username(" Noor Ali ") == "noor_ali"

If you want to test errors, use pytest.raises.

import pytest

def parse_age(value):
    value = value.strip()
    if not value.isdigit():
        raise ValueError("age must be a number")
    return int(value)

def test_parse_age_invalid():
    with pytest.raises(ValueError):
        parse_age("thirty")

Examples you can copy

Example 1: Run one test file or one test function

pytest test_math_utils.py
pytest test_math_utils.py::test_adds_two_numbers

Example 2: Test a function that returns a dict

def build_profile(name, city):
    return {"name": name, "city": city}

def test_build_profile():
    assert build_profile("Amina", "Boston") == {
        "name": "Amina",
        "city": "Boston",
    }

Example 3: Test string output with a clear diff

def greet(name):
    return f"Hello, {name}"

def test_greet():
    assert greet("Luka") == "Hello, Luka"

If this fails, Pytest shows a helpful diff between expected and actual output.


Example 4: Use a temporary folder for file tests

Pytest provides a built-in tmp_path fixture.

def test_write_temp_file(tmp_path):
    p = tmp_path / "note.txt"
    p.write_text("hi", encoding="utf-8")

    assert p.read_text(encoding="utf-8") == "hi"

Example 5: Mark and run a subset of tests

import pytest

@pytest.mark.slow
def test_slow_operation():
    assert sum(range(10_000)) > 0

Run only that group:

pytest -m slow

Example 6: Share setup with fixtures

import pytest

@pytest.fixture
def sample_users():
    return ["Amina", "Luka", "Noor"]

def test_has_three_users(sample_users):
    assert len(sample_users) == 3

Fixtures help you avoid repeating setup code.


Example 7: Test multiple cases with parametrize

import pytest

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

@pytest.mark.parametrize("n,expected", [
    (0, True),
    (1, False),
    (2, True),
    (11, False),
])
def test_is_even(n, expected):
    assert is_even(n) == expected

parametrize lets you test many inputs with one test function.


Common mistakes and how to fix them

Mistake 1: Pytest collects 0 tests

What you might do

  • Name a file tests.py
  • Name a test function adds_two_numbers()

Why it breaks

Pytest discovery relies on:

  • File: test_*.py or *_test.py
  • Function: test_*

Corrected approach

# test_numbers.py

def test_adds_two_numbers():
    assert 2 + 3 == 5

Mistake 2: Using print() instead of assertions

What you might do

def test_total():
    total = 2 + 2
    print(total)

Why it breaks

A test can pass without checking anything.

Corrected approach

def test_total():
    assert 2 + 2 == 4

Troubleshooting

If you see pytest: command not found, run it through Python:

python -m pytest

If you see ModuleNotFoundError for your own code, run Pytest from the project root folder, where your package or module can be imported.

If you see “collected 0 items,” check the naming:

  • File: test_*.py or *_test.py
  • Function: test_*

If tests pass locally but fail elsewhere, compare Python versions:

python --version

Then confirm you installed dependencies in the environment that runs the tests.

If you want to focus on one failure, run a single test with verbose output:

pytest path/to/test_file.py::test_name -v

Quick recap

  • Name files test_*.py and functions test_* so Pytest finds them.
  • Run tests with pytest or python -m pytest.
  • Use plain assert to check behavior, and pytest.raises for errors.
  • Use fixtures and parametrize when you want a cleaner setup or more coverage.