Course page
Log in

You are watching a free preview. Enroll to open all 24 lessons, track progress and ask the course tutor.

Enroll for $44.99

Section 1: pytest essentials

Your first tests and reading failures

Article 12 min Lesson 2 of 24 Preview

pytest finds tests by convention. Files named test_*.py or *_test.py, functions named test_*, and classes named Test* without an __init__ method. Create tests/test_totals.py:

from billable.totals import line_total, invoice_total

def test_line_total_multiplies_quantity_and_price():
    assert line_total(quantity=3, unit_price_cents=250) == 750

def test_invoice_total_sums_lines():
    lines = [(1, 1000), (2, 500)]
    assert invoice_total(lines) == 2000

Run the suite from the project root:

pytest

Each dot in the output is a passing test. An F is a failure, an E is an error during setup.

Reading a failure

Change the expected value in the second test to 2100 and run again. pytest shows the failing line, the values on both sides of the comparison and, for collections, a diff of what differs. This is the assertion rewriting feature: you write a plain assert, and pytest explains the failure in detail without special methods like assertEqual.

Useful command line options

  • pytest -x stops at the first failure.
  • pytest -k discount runs only tests whose names match "discount".
  • pytest tests/test_totals.py::test_invoice_total_sums_lines runs one test.
  • pytest -q gives quieter output, -v more detailed output.
  • pytest --lf reruns only the tests that failed last time.

Name tests as sentences

A test name like test_total tells you nothing when it fails. test_invoice_total_sums_lines describes the behaviour, so a failing test name in a CI log already tells you what broke.

Arrange, act, assert

Structure each test in three parts: set up the inputs, call the code, check the result. Keep one behaviour per test. Several asserts are fine if they check the same behaviour, but if a test needs a long name with "and" in it, split it.

Configuration

Add a [tool.pytest.ini_options] section to pyproject.toml with testpaths = ["tests"] and addopts = "-ra". The -ra flag prints a short summary of skipped and failed tests at the end.

Write three more tests for line_total, including a quantity of zero. One of them should fail. That is our first bug.

← → switch lessons