or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdconfiguration.mdfixtures.mdindex.mdmarks.mdreporting.mdtest-collection.mdtest-utilities.mdtesting-functions.mdwarnings.md

reporting.mddocs/

0

# Test Reporting and Results

1

2

Comprehensive reporting system for test execution results, collection reports, and terminal output formatting. These classes provide detailed information about test outcomes and handle presentation of results to users.

3

4

## Capabilities

5

6

### Test Execution Reports

7

8

Classes representing the results of test execution.

9

10

```python { .api }

11

@dataclasses.dataclass

12

class CallInfo(Generic[TResult]):

13

"""Result/Exception info of a function invocation."""

14

15

def __init__(

16

self,

17

result: TResult | None,

18

excinfo: ExceptionInfo[BaseException] | None,

19

start: float,

20

stop: float,

21

duration: float,

22

when: Literal["collect", "setup", "call", "teardown"],

23

_ispytest: bool = False,

24

) -> None:

25

"""

26

Initialize call information.

27

28

Parameters:

29

- result: Return value (None if exception occurred)

30

- excinfo: Exception info if raised

31

- start: Start time (seconds since epoch)

32

- stop: End time (seconds since epoch)

33

- duration: Call duration in seconds

34

- when: Invocation context

35

- _ispytest: Internal pytest flag

36

"""

37

38

# Attributes

39

_result: TResult | None # Private result storage

40

excinfo: ExceptionInfo[BaseException] | None # Captured exception

41

start: float # Start timestamp

42

stop: float # End timestamp

43

duration: float # Execution duration

44

when: str # Context of invocation ("collect", "setup", "call", "teardown")

45

46

@property

47

def result(self) -> TResult:

48

"""

49

Get the call result.

50

51

Returns:

52

The function result

53

54

Raises:

55

AttributeError: If excinfo is not None (exception occurred)

56

"""

57

58

@classmethod

59

def from_call(

60

cls,

61

func: Callable[[], TResult],

62

when: str,

63

reraise=None

64

) -> CallInfo[TResult]:

65

"""

66

Execute function and capture call information.

67

68

Parameters:

69

- func: Function to execute

70

- when: Context identifier

71

- reraise: Exception types to reraise immediately

72

73

Returns:

74

CallInfo with execution results and timing

75

"""

76

77

class TestReport(BaseReport):

78

"""Basic test report object for test execution results.

79

80

Also used for setup and teardown calls if they fail.

81

Reports can contain arbitrary extra attributes.

82

"""

83

84

def __init__(

85

self,

86

nodeid: str,

87

location: tuple[str, int | None, str],

88

keywords: Mapping[str, Any],

89

outcome: Literal["passed", "failed", "skipped"],

90

longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr,

91

when: Literal["setup", "call", "teardown"],

92

sections: Iterable[tuple[str, str]] = (),

93

duration: float = 0,

94

start: float = 0,

95

stop: float = 0,

96

user_properties: Iterable[tuple[str, object]] | None = None,

97

**extra

98

) -> None:

99

"""

100

Initialize test report.

101

102

Parameters:

103

- nodeid: Normalized collection nodeid

104

- location: File path, line number, domain info

105

- keywords: Name->value dict of keywords/markers

106

- outcome: Test outcome ("passed", "failed", "skipped")

107

- longrepr: Failure representation

108

- when: Test phase ("setup", "call", "teardown")

109

- sections: Extra information tuples (heading, content)

110

- duration: Test execution time in seconds

111

- start: Start time (seconds since epoch)

112

- stop: End time (seconds since epoch)

113

- user_properties: User-defined properties

114

- **extra: Additional arbitrary attributes

115

"""

116

117

# Core attributes

118

nodeid: str # Test identifier

119

location: tuple[str, int | None, str] # Test location info

120

keywords: Mapping[str, Any] # Associated keywords and markers

121

outcome: str # One of "passed", "failed", "skipped"

122

longrepr # Failure representation (None for passed tests)

123

when: str # Phase: "setup", "call", or "teardown"

124

user_properties: list[tuple[str, object]] # User-defined test properties

125

sections: list[tuple[str, str]] # Captured output sections

126

duration: float # Execution duration

127

start: float # Start timestamp

128

stop: float # End timestamp

129

wasxfail: str # XFail reason (if applicable)

130

131

@classmethod

132

def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport:

133

"""

134

Create TestReport from test item and call info.

135

136

Parameters:

137

- item: Test item that was executed

138

- call: Call information from execution

139

140

Returns:

141

TestReport with complete execution information

142

"""

143

144

def _to_json(self) -> dict[str, Any]:

145

"""

146

Serialize report to JSON-compatible dict.

147

148

Returns:

149

Dictionary representation for JSON serialization

150

"""

151

152

@classmethod

153

def _from_json(cls, reportdict: dict[str, object]) -> TestReport:

154

"""

155

Deserialize TestReport from JSON dict.

156

157

Parameters:

158

- reportdict: Dictionary from JSON deserialization

159

160

Returns:

161

TestReport instance

162

"""

163

164

class CollectReport(BaseReport):

165

"""Collection report object representing collection results.

166

167

Reports can contain arbitrary extra attributes.

168

"""

169

170

def __init__(

171

self,

172

nodeid: str,

173

outcome: Literal["passed", "failed", "skipped"],

174

longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr,

175

result: list[Item | Collector] | None,

176

sections: Iterable[tuple[str, str]] = (),

177

**extra

178

) -> None:

179

"""

180

Initialize collection report.

181

182

Parameters:

183

- nodeid: Normalized collection nodeid

184

- outcome: Collection outcome ("passed", "failed", "skipped")

185

- longrepr: Failure representation

186

- result: Collected items and nodes

187

- sections: Extra information sections

188

- **extra: Additional arbitrary attributes

189

"""

190

191

# Attributes

192

when = "collect" # Always "collect" for collection reports

193

nodeid: str # Collection node identifier

194

outcome: str # Collection result

195

longrepr # Collection failure details (if any)

196

result: list[Item | Collector] # Successfully collected items

197

sections: list[tuple[str, str]] # Captured output during collection

198

199

@property

200

def location(self) -> tuple[str, None, str]:

201

"""

202

Get collection location.

203

204

Returns:

205

Tuple of (fspath, None, fspath)

206

"""

207

208

def _to_json(self) -> dict[str, Any]:

209

"""

210

Serialize report to JSON-compatible dict.

211

212

Returns:

213

Dictionary representation for JSON serialization

214

"""

215

216

@classmethod

217

def _from_json(cls, reportdict: dict[str, object]) -> CollectReport:

218

"""

219

Deserialize CollectReport from JSON dict.

220

221

Parameters:

222

- reportdict: Dictionary from JSON deserialization

223

224

Returns:

225

CollectReport instance

226

"""

227

```

228

229

### Terminal Output and Formatting

230

231

Classes controlling terminal output during test execution.

232

233

```python { .api }

234

class TestShortLogReport(NamedTuple):

235

"""Container for test status information used in terminal reporting.

236

237

Used to store the test status result category, shortletter and verbose word.

238

For example "rerun", "R", ("RERUN", {"yellow": True}).

239

"""

240

241

category: str # Result class ("passed", "skipped", "error", or empty)

242

letter: str # Short letter for progress (".", "s", "E", or empty)

243

word: str | tuple[str, Mapping[str, bool]] # Verbose word or (word, markup) tuple

244

245

class TerminalReporter:

246

"""Controls terminal output formatting and reporting during test execution.

247

248

Main class responsible for all terminal output including progress indicators,

249

test results, summaries, and formatting.

250

"""

251

252

def __init__(self, config: Config, file: TextIO | None = None) -> None:

253

"""

254

Initialize terminal reporter.

255

256

Parameters:

257

- config: pytest configuration object

258

- file: Output stream (defaults to sys.stdout)

259

"""

260

261

# Core attributes

262

config: Config # pytest configuration

263

stats: dict[str, list[Any]] # Statistics by category (failed, passed, etc.)

264

_tw: TerminalWriter # Terminal writer for output

265

reportchars: str # Characters controlling what to report

266

hasmarkup: bool # Whether terminal supports markup

267

isatty: bool # Whether output is to a terminal

268

verbosity: int # Verbosity level

269

showfspath: bool # Whether to show file paths

270

showlongtestinfo: bool # Whether to show detailed test info

271

_session: Session | None # Current test session

272

_numcollected: int # Number of collected items

273

currentfspath: Path | str | int | None # Current file being processed

274

275

def write(self, content: str, *, flush: bool = False, **markup: bool) -> None:

276

"""

277

Write content to terminal.

278

279

Parameters:

280

- content: Content to write

281

- flush: Whether to flush output immediately

282

- **markup: Markup options (bold, red, etc.)

283

"""

284

285

def write_line(self, line: str | bytes, **markup: bool) -> None:

286

"""

287

Write line with newline to terminal.

288

289

Parameters:

290

- line: Line content

291

- **markup: Markup options

292

"""

293

294

def write_sep(self, sep: str, title: str | None = None, **markup: bool) -> None:

295

"""

296

Write separator line.

297

298

Parameters:

299

- sep: Separator character

300

- title: Optional title to include

301

- **markup: Markup options

302

"""

303

304

def rewrite(self, line: str, **markup: bool) -> None:

305

"""

306

Rewrite current line (for progress indicators).

307

308

Parameters:

309

- line: New line content

310

- **markup: Markup options

311

"""

312

313

def section(self, title: str, sep: str = "=", **kw: bool) -> None:

314

"""

315

Write section header.

316

317

Parameters:

318

- title: Section title

319

- sep: Separator character

320

- **kw: Keyword arguments for formatting

321

"""

322

323

def pytest_runtest_logreport(self, report: TestReport) -> None:

324

"""

325

Handle test report logging.

326

327

Parameters:

328

- report: Test execution report

329

"""

330

331

def pytest_collectreport(self, report: CollectReport) -> None:

332

"""

333

Handle collection report logging.

334

335

Parameters:

336

- report: Collection report

337

"""

338

339

def pytest_sessionstart(self, session: Session) -> None:

340

"""

341

Handle session start.

342

343

Parameters:

344

- session: Test session

345

"""

346

347

def pytest_sessionfinish(self, session: Session, exitstatus: int | ExitCode) -> None:

348

"""

349

Handle session finish.

350

351

Parameters:

352

- session: Test session

353

- exitstatus: Exit status code

354

"""

355

356

def summary_failures(self) -> None:

357

"""Show failure summary section."""

358

359

def summary_errors(self) -> None:

360

"""Show error summary section."""

361

362

def summary_warnings(self) -> None:

363

"""Show warning summary section."""

364

365

def short_test_summary(self) -> None:

366

"""Show short test summary section."""

367

368

def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]:

369

"""

370

Build final statistics line.

371

372

Returns:

373

Tuple of (stat_parts, duration_string)

374

"""

375

```

376

377

## Usage Examples

378

379

### Creating Custom Reports

380

381

```python

382

import pytest

383

from _pytest.reports import TestReport, CollectReport

384

385

# Custom plugin to process test reports

386

class CustomReportPlugin:

387

def __init__(self):

388

self.test_results = []

389

390

def pytest_runtest_logreport(self, report: TestReport):

391

# Process test reports

392

if report.when == "call": # Only process main test execution

393

self.test_results.append({

394

'nodeid': report.nodeid,

395

'outcome': report.outcome,

396

'duration': report.duration,

397

'location': report.location,

398

'user_properties': report.user_properties,

399

})

400

401

def pytest_collection_finish(self, session):

402

# Process collection results

403

for item in session.items:

404

print(f"Collected test: {item.nodeid}")

405

406

# Register plugin

407

pytest.main(["-p", "no:terminal", "--tb=no"], plugins=[CustomReportPlugin()])

408

```

409

410

### Custom Terminal Output

411

412

```python

413

from _pytest.terminal import TerminalReporter

414

415

class ColorfulReporter(TerminalReporter):

416

def pytest_runtest_logreport(self, report):

417

# Custom colored output

418

if report.when == "call":

419

if report.outcome == "passed":

420

self.write("✓ ", green=True)

421

elif report.outcome == "failed":

422

self.write("✗ ", red=True)

423

elif report.outcome == "skipped":

424

self.write("- ", yellow=True)

425

426

self.write_line(f"{report.nodeid}")

427

428

# Call parent implementation for other functionality

429

super().pytest_runtest_logreport(report)

430

431

# Use custom reporter

432

def pytest_configure(config):

433

if config.pluginmanager.get_plugin("terminalreporter"):

434

config.pluginmanager.unregister(name="terminalreporter")

435

436

config.pluginmanager.register(ColorfulReporter(config), "terminalreporter")

437

```

438

439

### Accessing Call Information

440

441

```python

442

from _pytest.runner import CallInfo

443

444

def test_with_call_info():

445

# CallInfo is typically used internally, but can be accessed in hooks

446

def example_function():

447

return "result"

448

449

# Capture call information

450

call_info = CallInfo.from_call(example_function, when="call")

451

452

assert call_info.result == "result"

453

assert call_info.excinfo is None

454

assert call_info.duration > 0

455

assert call_info.when == "call"

456

457

# Plugin to access call information in hooks

458

def pytest_runtest_call(pyfuncitem):

459

# This hook has access to CallInfo through the test execution

460

pass

461

462

def pytest_runtest_makereport(item, call):

463

# call parameter is a CallInfo object

464

if call.when == "call" and call.excinfo is None:

465

print(f"Test {item.nodeid} passed in {call.duration:.2f}s")

466

```

467

468

## Integration with Reporting Tools

469

470

### JSON Report Generation

471

472

```python

473

import json

474

from _pytest.reports import TestReport, CollectReport

475

476

class JSONReporter:

477

def __init__(self):

478

self.reports = []

479

480

def pytest_runtest_logreport(self, report: TestReport):

481

# Convert TestReport to JSON-serializable format

482

report_data = report._to_json()

483

self.reports.append(report_data)

484

485

def pytest_sessionfinish(self, session):

486

# Write JSON report

487

with open("test_report.json", "w") as f:

488

json.dump(self.reports, f, indent=2)

489

490

# Usage

491

pytest.main(["--tb=short"], plugins=[JSONReporter()])

492

```

493

494

### Database Integration

495

496

```python

497

import sqlite3

498

from _pytest.reports import TestReport

499

500

class DatabaseReporter:

501

def __init__(self):

502

self.conn = sqlite3.connect("test_results.db")

503

self.setup_database()

504

505

def setup_database(self):

506

self.conn.execute("""

507

CREATE TABLE IF NOT EXISTS test_results (

508

nodeid TEXT,

509

outcome TEXT,

510

duration REAL,

511

when_phase TEXT,

512

timestamp REAL,

513

location_file TEXT,

514

location_line INTEGER

515

)

516

""")

517

518

def pytest_runtest_logreport(self, report: TestReport):

519

if report.when == "call":

520

self.conn.execute("""

521

INSERT INTO test_results

522

(nodeid, outcome, duration, when_phase, timestamp, location_file, location_line)

523

VALUES (?, ?, ?, ?, ?, ?, ?)

524

""", (

525

report.nodeid,

526

report.outcome,

527

report.duration,

528

report.when,

529

report.start,

530

report.location[0],

531

report.location[1]

532

))

533

self.conn.commit()

534

535

# Usage with database integration

536

pytest.main(["-v"], plugins=[DatabaseReporter()])

537

```