The CheckOps Engineer home

Tools directory

P

The CheckOps Engineer

pytest

pytest is the usual Python test runner: plain asserts, fixtures, parametrization, and a large plugin set.

Visit website

Latest public signals

Not refreshed yet. Refresh reads the public site and, when the entry has a GitHub repository, the latest release.

Visit Website

Introduction

pytest is the test runner most Python projects use. You write functions named test_*, use the language’s assert, and pytest rewrites those asserts so failures show the values involved. Setup and teardown live in fixtures, which tests request by naming a parameter. The core project is MIT-licensed at pytest-dev/pytest.

The runner collects tests from files, classes, and directories, then runs them in one process by default. Plugins add parallel runs, coverage, Django and Flask integration, asyncio, and random order. pytest does not drive a browser or a mobile device by itself. Those are separate plugins or other tools.

Features

  1. Assert rewriting: A failing assert response.status_code == 200 prints both sides. You do not call self.assertEqual.
  2. Fixtures: A fixture in conftest.py or a test module is injected by parameter name. Scope can be function, class, module, package, or session.
  3. Parametrize: @pytest.mark.parametrize runs one test body against a table of inputs and expected values.
  4. Markers: @pytest.mark.slow and custom markers select or skip tests from the command line (-m "not slow").
  5. Plugins: pytest-xdist runs workers in parallel. pytest-cov reports coverage. Framework plugins load app fixtures.
  6. Fixtures as resources: tmp_path, monkeypatch, capsys, and capfd are built in. Third-party fixtures start databases and HTTP servers.
  7. Output: Short tracebacks, a summary of failures, and --junitxml for CI. -k selects tests by name expression.
  8. Doctest and unittest: pytest collects unittest.TestCase classes and can run doctests, so older suites do not have to be rewritten on day one.

Pros

  1. Little boilerplate: A test is a function and an assert. New contributors can read a file without learning an assertion API.
  2. Fixtures compose: Database, client, and factory fixtures stack by name instead of a deep setUp inheritance tree.
  3. Plugin coverage: Django, Flask, asyncio, httpx, and hypothesis integrations already exist. You rarely write the glue yourself.
  4. Failure output is specific: Rewritten asserts and fixture scopes make the failing value and the setup that produced it visible.
  5. Free and standard: MIT-licensed, packaged on PyPI, and expected by Python CI templates.

Cons

  1. Fixture graphs get opaque: A test with five parameters hides where the database was created. Large conftest.py files are hard to trace.
  2. Collection has a cost: Importing every test module runs top-level code. A slow import or a fixture with session scope can dominate a small change.
  3. Plugins conflict: Two plugins that both wrap pytest_runtest_call fail in non-obvious ways. Pin versions.
  4. Parallelism is not free: pytest-xdist breaks tests that share a process-wide resource unless they were written for it.
  5. Not a system-test tool: Browser, mobile, and load tests need other runners. pytest can launch them, but it does not replace them.
pip install pytest
pytest
pytest -k "login and not slow"
pytest --junitxml=report.xml
import pytest

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

@pytest.mark.parametrize("a,b,expected", [(1, 2, 3), (0, 0, 0)])
def test_add(a, b, expected):
    assert add(a, b) == expected

Put shared fixtures in conftest.py next to the tests that use them. A fixture is only instantiated when a test asks for it by name.

Resources:

Pricing

  1. Free: pytest is MIT-licensed and distributed on PyPI. There is no pytest company plan.
  2. Plugins are separate: Most plugins are also free. A few commercial Python tools wrap pytest; those prices are not pytest’s.
  1. Python services and libraries that need unit and integration tests in the same runner.
  2. Django, Flask, and FastAPI projects that already have a pytest plugin for the app client and database.
  3. Data and ML code written in Python, including suites that call DeepEval or similar pytest-style evals.
  4. Teams migrating off unittest who want to keep existing TestCase classes while new tests use functions.

Conclusion

pytest is the Python runner to reach for unless a framework forces another one. Fixtures and parametrization cover most unit and service tests, and the plugin set covers the frameworks around them. Keep fixture scope explicit, and do not expect pytest alone to cover browser or device testing.