Skip to content

Events

All events emitted during test execution. Events are defined in protest/events/types.py.

Session Lifecycle

Event Data When
COLLECTION_FINISH list[TestItem] After collection, before execution
SESSION_START None Before any test runs
SESSION_SETUP_DONE SessionSetupInfo After session fixtures resolved
SESSION_TEARDOWN_START None Before session fixture teardown
SESSION_END SessionResult After teardown complete
SESSION_COMPLETE SessionResult After all async handlers finished
SESSION_INTERRUPTED bool (force_teardown) On Ctrl+C

Suite Lifecycle

Event Data When
SUITE_START SuiteStartInfo Before suite's first test
SUITE_SETUP_DONE SuiteSetupInfo After suite fixtures resolved
SUITE_TEARDOWN_START str (suite_path) Before suite fixture teardown
SUITE_END SuiteResult After teardown complete

Test Lifecycle

Event Data When
TEST_START TestStartInfo Test queued for execution
TEST_ACQUIRED TestStartInfo Test acquired execution slot
TEST_SETUP_DONE TestStartInfo Fixtures resolved, before test body
TEST_TEARDOWN_START TestTeardownInfo After test body, before teardown
TEST_RETRY TestRetryInfo Test failed, will retry

Test Outcomes

Event Data When
TEST_PASS TestResult Test passed
TEST_FAIL TestResult Test failed
TEST_SKIP TestResult Test skipped
TEST_XFAIL TestResult Expected failure
TEST_XPASS TestResult Unexpected pass (xfail test)

Fixture Lifecycle

Event Data When
FIXTURE_SETUP_START FixtureInfo Before fixture setup
FIXTURE_SETUP_DONE FixtureInfo After fixture setup
FIXTURE_TEARDOWN_START FixtureInfo Before fixture teardown
FIXTURE_TEARDOWN_DONE FixtureInfo After fixture teardown

Meta Events

Event Data When
HANDLER_START HandlerInfo Before handler executes
HANDLER_END HandlerInfo After handler completes
WAITING_HANDLERS int (count) Waiting for async handlers

Event Sequence

COLLECTION_FINISH (emit_and_collect)
SESSION_START
│  ┌─────────────────────────────────────── for each session fixture
│  │ FIXTURE_SETUP_START (scope=session)
│  │ FIXTURE_SETUP_DONE (scope=session)
│  └───────────────────────────────────────
SESSION_SETUP_DONE
├─ SUITE_START ("API") ◄─────────────────── for each suite
│  │
│  │  ┌──────────────────────────────────── for each suite fixture
│  │  │ FIXTURE_SETUP_START (scope=suite)
│  │  │ FIXTURE_SETUP_DONE (scope=suite)
│  │  └────────────────────────────────────
│  │
│  │ SUITE_SETUP_DONE
│  │
│  ├─ TEST_START ◄───────────────────────── for each test
│  │  │ TEST_ACQUIRED
│  │  │  ┌───────────────────────────────── for each test fixture
│  │  │  │ FIXTURE_SETUP_START (scope=test)
│  │  │  │ FIXTURE_SETUP_DONE (scope=test)
│  │  │  └─────────────────────────────────
│  │  │ TEST_SETUP_DONE
│  │  │ ... test body ...
│  │  │ TEST_TEARDOWN_START
│  │  │  ┌───────────────────────────────── for each test fixture (LIFO)
│  │  │  │ FIXTURE_TEARDOWN_START
│  │  │  │ FIXTURE_TEARDOWN_DONE
│  │  │  └─────────────────────────────────
│  │  └─ TEST_PASS / TEST_FAIL / TEST_SKIP
│  │
│  │ SUITE_TEARDOWN_START
│  │  ┌──────────────────────────────────── for each suite fixture (LIFO)
│  │  │ FIXTURE_TEARDOWN_START
│  │  │ FIXTURE_TEARDOWN_DONE
│  │  └────────────────────────────────────
│  └─ SUITE_END
SESSION_TEARDOWN_START
│  ┌─────────────────────────────────────── for each session fixture (LIFO)
│  │ FIXTURE_TEARDOWN_START
│  │ FIXTURE_TEARDOWN_DONE
│  └───────────────────────────────────────
SESSION_END
WAITING_HANDLERS (if pending)
SESSION_COMPLETE

Notes:

  • Fixture teardown is LIFO (last setup = first teardown)
  • Tests may run in parallel (events interleaved)
  • Non-autouse fixtures are setup on first use, then cached

Data Classes

All defined in protest/entities/events.py.

TestResult

@dataclass(frozen=True, slots=True)
class TestResult:
    name: str
    node_id: str = ""
    suite_path: str | None = None
    error: Exception | None = None
    duration: float = 0
    output: str = ""
    is_fixture_error: bool = False
    skip_reason: str | None = None
    xfail_reason: str | None = None
    timeout: float | None = None
    attempt: int = 1
    max_attempts: int = 1
    previous_errors: tuple[Exception, ...] = ()

SessionResult

@dataclass(frozen=True, slots=True)
class SessionResult:
    passed: int
    failed: int
    errors: int = 0
    skipped: int = 0
    xfailed: int = 0
    xpassed: int = 0
    duration: float = 0
    setup_duration: float = 0
    teardown_duration: float = 0
    interrupted: bool = False

SuiteResult

@dataclass(frozen=True, slots=True)
class SuiteResult:
    name: str
    duration: float = 0
    setup_duration: float = 0
    teardown_duration: float = 0

SessionSetupInfo

@dataclass(frozen=True, slots=True)
class SessionSetupInfo:
    duration: float

SuiteSetupInfo

@dataclass(frozen=True, slots=True)
class SuiteSetupInfo:
    name: str
    duration: float

TestStartInfo

@dataclass(frozen=True, slots=True)
class TestStartInfo:
    name: str
    node_id: str

TestTeardownInfo

@dataclass(frozen=True, slots=True)
class TestTeardownInfo:
    name: str
    node_id: str
    outcome: Event  # TEST_PASS, TEST_FAIL, etc.

TestRetryInfo

@dataclass(frozen=True, slots=True)
class TestRetryInfo:
    name: str
    node_id: str
    suite_path: str | None
    attempt: int
    max_attempts: int
    error: Exception
    delay: float

FixtureInfo

@dataclass(frozen=True, slots=True)
class FixtureInfo:
    name: str
    scope: FixtureScope  # SESSION, SUITE, TEST
    scope_path: str | None = None
    duration: float = 0
    autouse: bool = False

HandlerInfo

@dataclass(frozen=True, slots=True)
class HandlerInfo:
    name: str
    event: Event
    is_async: bool
    duration: float = 0
    error: Exception | None = None

See Also