Test Lifecycle Management

Purpose

Test Lifecycle Management addresses the orchestration of running individual test items within pytest. This includes managing the sequence of phases each test undergoes—**setup**, **call** (execution), and **teardown**—as well as handling the outcomes of these phases (passing, failing, skipping, or erroring). It ensures that test resources are correctly initialized and cleaned up, errors are properly reported, and the test environment remains consistent throughout the test session.

This subtopic focuses on the precise control flow and state management required to execute tests reliably, which is essential for pytest’s robustness and user-friendly reporting but is not covered in the broader scope of Test Execution and Reporting.

Functionality

Core Workflow

Each test item follows a strict lifecycle with the phases:

  1. Setup: Prepare the test environment, including fixtures and any necessary state.

  2. Call: Execute the actual test function or method.

  3. Teardown: Clean up resources allocated during setup or test execution.

The lifecycle is initiated and coordinated primarily through the `runtestprotocol` function:

def runtestprotocol(item: Item, log: bool = True, nextitem: Item | None = None) -> list[TestReport]:
    rep = call_and_report(item, "setup", log)
    reports = [rep]
    if rep.passed:
        reports.append(call_and_report(item, "call", log))
    reports.append(call_and_report(item, "teardown", log, nextitem=nextitem))
    return reports

SetupState: Managing Setup and Teardown Stack

The `SetupState` class maintains a stack representing active test nodes (e.g., session, modules, functions) to manage setup and teardown efficiently:

This stack-based approach ensures that nested setups and teardowns occur correctly and that failures in setup propagate properly to dependent tests.

Outcome Handling and Reporting

Exception Interaction

Relationship to Parent Topic and Other Subtopics

Test Lifecycle Management is a foundational part of the **Test Execution and Reporting** topic, implementing the detailed mechanics behind running each test item. While the parent topic covers the overall execution and reporting, this subtopic dives into:

It complements **Terminal Reporting** and **JUnit XML Reporting** by producing the detailed reports those subtopics consume and display. It also interacts closely with **Fixture Management**, as fixture setup/teardown is integrated into the setup/teardown phases managed here.

Unlike the parent topic’s broad focus, this subtopic introduces the concept and implementation of the `SetupState` stack and the fine-grained lifecycle protocol, which are unique to managing test execution order and resource cleanup.

Diagram: Test Lifecycle Flowchart

flowchart TD
    A[Start Test Run] --> B[Setup Phase]
    B -->|Setup successful| C[Call Phase (Test Function Execution)]
    B -->|Setup failed| D[Skip Call Phase]
    C --> E[Teardown Phase]
    D --> E
    E --> F[Generate Test Reports]
    F --> G[End Test Run]

    style B fill:#a8d5e2,stroke:#333,stroke-width:1px
    style C fill:#a8e2a8,stroke:#333,stroke-width:1px
    style D fill:#f9d5a8,stroke:#333,stroke-width:1px
    style E fill:#e2a8d5,stroke:#333,stroke-width:1px
    style F fill:#d5e2a8,stroke:#333,stroke-width:1px

This flowchart illustrates the sequential phases of a test item’s lifecycle, emphasizing the conditional logic that skips the call phase if setup fails, and the mandatory teardown phase that always follows to ensure clean state.


This management of test lifecycle phases ensures pytest’s reliability and extensibility by clearly defining how tests are executed, how failures propagate, and how cleanup is guaranteed, forming the backbone for consistent test execution and reporting.