TechSchool
Home / Python3 / pytest Tutorial

pytest Tutorial

This page shows how pytest runs plain functions with the assert statement and reports the values on failure. It also covers fixtures, parametrize, built-in helpers, flags, a unittest comparison, and common mistakes.

Run a plain function with assert

pytest finds files named test_*.py and functions named test_*. You do not need a class. The assert statement checks a condition. When the condition is false, pytest shows the values of both sides.

Install pytest with pip. Then write a small test file. Run the command pytest in the same folder. The output shows the file, the line, and the values that failed.

pip install pytest

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

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

def test_add_fail():
    assert add(2, 3) == 6

Fixtures and conftest.py

A fixture is a function with the @pytest.fixture decorator. A test gets the fixture value by naming it as an argument. Use yield to give the value and to run teardown code after the test.

Put shared fixtures in a file named conftest.py. pytest loads this file automatically. Tests in the same folder can use the fixtures without an import.

The example below creates a list fixture. The test adds one item. After the test, the code after yield clears the list.

import pytest

@pytest.fixture
def items():
    data = [1, 2]
    yield data
    data.clear()

def test_add_item(items):
    items.append(3)
    assert items == [1, 2, 3]

Parametrize one test with many inputs

The @pytest.mark.parametrize decorator runs one test function many times. Each tuple in the list is one run. pytest reports each run as a separate test.

The example checks a square function with three inputs. pytest runs three tests and shows one result for each tuple. A failure names the exact input.

import pytest

def square(x):
    return x * x

@pytest.mark.parametrize("value,expected", [(2, 4), (3, 9), (0, 0)])
def test_square(value, expected):
    assert square(value) == expected

Built-in helpers and flags

The tmp_path fixture gives a fresh temporary folder for each test. The capsys fixture captures printed output. Use capsys.readouterr() to read the text.

The -k flag selects tests by name. The -x flag stops the run at the first failure. These flags help you focus on one problem at a time.

def test_print(capsys):
    print("hello")
    out = capsys.readouterr().out
    assert out == "hello\n"

def test_file(tmp_path):
    path = tmp_path / "data.txt"
    path.write_text("ok")
    assert path.read_text() == "ok"

Compare with unittest

unittest needs a class that inherits from TestCase. Each check uses a method such as assertEqual. pytest needs only a function and the assert statement, so it has less boilerplate.

pytest shows the values of a failed assert. unittest shows a message but often not the values. This makes a pytest failure faster to read.

unittest uses setUp and tearDown methods. pytest uses fixtures, and a fixture can return a value. A fixture can also run only when a test needs it.

Mistakes to avoid

Mistake one: a fixture hides a slow network call. The test looks fast but it waits on the network. Fix: use a local fake or a stub in the fixture. Keep the network call in a separate integration test.

Mistake two: tests pass alone and fail together. Shared state causes this problem. Fix: give each test its own data, and clean up in the fixture teardown. Run the full suite to find the order problem.

Common questions

Do I need to install pytest?

Yes, if pytest is not already in your environment. Run pip install pytest to add it.

What is conftest.py?

It is a file that pytest loads automatically for shared fixtures and hooks. Tests in the same folder can use its fixtures without an import.

How do I run a single pytest test?

Use the -k flag with a name, for example pytest -k test_add. You can also give the file and the test node, for example pytest test_math.py::test_add.

Continue learning
Python3 module Python3 class Python3 tutorial