or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-models.mdindex.mdplugin-system.mdtest-decorators.mdtest-lifecycle.mdutilities.md

plugin-system.mddocs/

0

# Plugin System

1

2

Hook-based plugin architecture enabling extensible test reporting with separate hooks for end users and adapter developers. Built on pluggy, the system provides a flexible way to extend Allure functionality and integrate with different testing frameworks.

3

4

## Capabilities

5

6

### Plugin Manager

7

8

Singleton plugin manager providing access to the hook system and plugin registration.

9

10

```python { .api }

11

class plugin_manager:

12

"""

13

Singleton plugin manager built on pluggy.

14

15

Provides access to hook system for registering plugins and calling hooks.

16

Automatically registers AllureUserHooks and AllureDeveloperHooks.

17

"""

18

19

# All PluginManager methods are available via metaclass delegation

20

# Key methods include:

21

# - register(plugin): Register a plugin instance

22

# - unregister(plugin): Unregister a plugin instance

23

# - get_plugins(): Get all registered plugins

24

# - hook: Access to hook calling interface

25

26

class MetaPluginManager(type):

27

"""Metaclass managing plugin manager singleton."""

28

29

@staticmethod

30

def get_plugin_manager():

31

"""

32

Get or create the plugin manager instance.

33

34

Returns:

35

PluginManager: Configured pluggy PluginManager instance

36

"""

37

```

38

39

**Usage Examples:**

40

41

```python

42

from allure_commons import plugin_manager

43

44

# Register a custom plugin

45

class MyAllurePlugin:

46

@allure_commons.hookimpl

47

def report_result(self, result):

48

# Custom result processing

49

print(f"Test completed: {result.name}")

50

51

# Register the plugin

52

my_plugin = MyAllurePlugin()

53

plugin_manager.register(my_plugin)

54

55

# Call hooks (usually done by the framework)

56

plugin_manager.hook.report_result(result=test_result)

57

```

58

59

### Hook Specifications

60

61

Two categories of hooks: user hooks for test enhancement and developer hooks for framework integration.

62

63

### User Hooks (AllureUserHooks)

64

65

Hooks for test decoration and runtime enhancement, primarily used by test authors.

66

67

```python { .api }

68

class AllureUserHooks:

69

"""Hook specifications for end-user test enhancement."""

70

71

@hookspec

72

def decorate_as_title(self, test_title):

73

"""

74

Hook for decorating tests with titles.

75

76

Parameters:

77

- test_title (str): Title to apply to test

78

79

Returns:

80

Function decorator or None

81

"""

82

83

@hookspec

84

def add_title(self, test_title):

85

"""

86

Hook for adding titles at runtime.

87

88

Parameters:

89

- test_title (str): Title to add to current test

90

"""

91

92

@hookspec

93

def decorate_as_description(self, test_description):

94

"""

95

Hook for decorating tests with descriptions.

96

97

Parameters:

98

- test_description (str): Description to apply to test

99

100

Returns:

101

Function decorator or None

102

"""

103

104

@hookspec

105

def add_description(self, test_description):

106

"""

107

Hook for adding descriptions at runtime.

108

109

Parameters:

110

- test_description (str): Description to add to current test

111

"""

112

113

@hookspec

114

def decorate_as_description_html(self, test_description_html):

115

"""

116

Hook for decorating tests with HTML descriptions.

117

118

Parameters:

119

- test_description_html (str): HTML description to apply

120

121

Returns:

122

Function decorator or None

123

"""

124

125

@hookspec

126

def add_description_html(self, test_description_html):

127

"""

128

Hook for adding HTML descriptions at runtime.

129

130

Parameters:

131

- test_description_html (str): HTML description to add

132

"""

133

134

@hookspec

135

def decorate_as_label(self, label_type, labels):

136

"""

137

Hook for decorating tests with labels.

138

139

Parameters:

140

- label_type (str): Type of label

141

- labels (tuple): Label values

142

143

Returns:

144

Function decorator or None

145

"""

146

147

@hookspec

148

def add_label(self, label_type, labels):

149

"""

150

Hook for adding labels at runtime.

151

152

Parameters:

153

- label_type (str): Type of label

154

- labels (tuple): Label values

155

"""

156

157

@hookspec

158

def decorate_as_link(self, url, link_type, name):

159

"""

160

Hook for decorating tests with links.

161

162

Parameters:

163

- url (str): Link URL

164

- link_type (str): Type of link

165

- name (str): Display name for link

166

167

Returns:

168

Function decorator or None

169

"""

170

171

@hookspec

172

def add_link(self, url, link_type, name):

173

"""

174

Hook for adding links at runtime.

175

176

Parameters:

177

- url (str): Link URL

178

- link_type (str): Type of link

179

- name (str): Display name for link

180

"""

181

182

@hookspec

183

def add_parameter(self, name, value, excluded, mode):

184

"""

185

Hook for adding parameters at runtime.

186

187

Parameters:

188

- name (str): Parameter name

189

- value: Parameter value

190

- excluded (bool): Whether to exclude from display

191

- mode (ParameterMode): Display mode

192

"""

193

194

@hookspec

195

def start_step(self, uuid, title, params):

196

"""

197

Hook for starting test steps.

198

199

Parameters:

200

- uuid (str): Step UUID

201

- title (str): Step title

202

- params (dict): Step parameters

203

"""

204

205

@hookspec

206

def stop_step(self, uuid, exc_type, exc_val, exc_tb):

207

"""

208

Hook for stopping test steps.

209

210

Parameters:

211

- uuid (str): Step UUID

212

- exc_type: Exception type if step failed

213

- exc_val: Exception value if step failed

214

- exc_tb: Exception traceback if step failed

215

"""

216

217

@hookspec

218

def attach_data(self, body, name, attachment_type, extension):

219

"""

220

Hook for attaching data to tests.

221

222

Parameters:

223

- body (str or bytes): Data to attach

224

- name (str): Attachment display name

225

- attachment_type: MIME type or AttachmentType

226

- extension (str): File extension

227

"""

228

229

@hookspec

230

def attach_file(self, source, name, attachment_type, extension):

231

"""

232

Hook for attaching files to tests.

233

234

Parameters:

235

- source (str): Path to source file

236

- name (str): Attachment display name

237

- attachment_type: MIME type or AttachmentType

238

- extension (str): File extension

239

"""

240

```

241

242

### Developer Hooks (AllureDeveloperHooks)

243

244

Hooks for framework integration and test lifecycle management, used by testing framework adapters.

245

246

```python { .api }

247

class AllureDeveloperHooks:

248

"""Hook specifications for framework adapter developers."""

249

250

@hookspec

251

def start_fixture(self, parent_uuid, uuid, name, parameters):

252

"""

253

Hook for starting fixture execution.

254

255

Parameters:

256

- parent_uuid (str): Parent container UUID

257

- uuid (str): Fixture UUID

258

- name (str): Fixture name

259

- parameters (dict): Fixture parameters

260

"""

261

262

@hookspec

263

def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb):

264

"""

265

Hook for stopping fixture execution.

266

267

Parameters:

268

- parent_uuid (str): Parent container UUID

269

- uuid (str): Fixture UUID

270

- name (str): Fixture name

271

- exc_type: Exception type if fixture failed

272

- exc_val: Exception value if fixture failed

273

- exc_tb: Exception traceback if fixture failed

274

"""

275

276

@hookspec

277

def start_test(self, parent_uuid, uuid, name, parameters, context):

278

"""

279

Hook for starting test execution.

280

281

Parameters:

282

- parent_uuid (str): Parent container UUID

283

- uuid (str): Test UUID

284

- name (str): Test name

285

- parameters (dict): Test parameters

286

- context (dict): Test execution context

287

"""

288

289

@hookspec

290

def stop_test(self, parent_uuid, uuid, name, context, exc_type, exc_val, exc_tb):

291

"""

292

Hook for stopping test execution.

293

294

Parameters:

295

- parent_uuid (str): Parent container UUID

296

- uuid (str): Test UUID

297

- name (str): Test name

298

- context (dict): Test execution context

299

- exc_type: Exception type if test failed

300

- exc_val: Exception value if test failed

301

- exc_tb: Exception traceback if test failed

302

"""

303

304

@hookspec

305

def report_result(self, result):

306

"""

307

Hook for reporting test results.

308

309

Parameters:

310

- result (TestResult): Complete test result object

311

"""

312

313

@hookspec

314

def report_container(self, container):

315

"""

316

Hook for reporting test containers.

317

318

Parameters:

319

- container (TestResultContainer): Test container object

320

"""

321

322

@hookspec

323

def report_attached_file(self, source, file_name):

324

"""

325

Hook for reporting attached files.

326

327

Parameters:

328

- source (str): Source file path

329

- file_name (str): Destination file name

330

"""

331

332

@hookspec

333

def report_attached_data(self, body, file_name):

334

"""

335

Hook for reporting attached data.

336

337

Parameters:

338

- body (str or bytes): Data content

339

- file_name (str): Destination file name

340

"""

341

```

342

343

### Hook Implementation

344

345

Decorators and markers for implementing hooks in plugins.

346

347

```python { .api }

348

# Hook specification marker

349

hookspec: HookspecMarker

350

351

# Hook implementation marker

352

hookimpl: HookimplMarker

353

```

354

355

**Usage Examples:**

356

357

```python

358

from allure_commons import hookimpl

359

360

class CustomReporter:

361

"""Custom reporter plugin example."""

362

363

@hookimpl

364

def report_result(self, result):

365

"""Custom test result processing."""

366

print(f"Test {result.name}: {result.status}")

367

368

# Send to external system

369

send_to_dashboard({

370

'test_name': result.name,

371

'status': result.status,

372

'duration': result.stop - result.start

373

})

374

375

@hookimpl

376

def report_container(self, container):

377

"""Custom container processing."""

378

print(f"Container {container.name} completed with {len(container.children)} tests")

379

380

@hookimpl

381

def attach_data(self, body, name, attachment_type, extension):

382

"""Custom attachment processing."""

383

if attachment_type == "application/json":

384

# Parse and validate JSON attachments

385

try:

386

data = json.loads(body)

387

print(f"Valid JSON attachment: {name}")

388

except json.JSONDecodeError:

389

print(f"Invalid JSON attachment: {name}")

390

391

class TestFrameworkAdapter:

392

"""Example testing framework adapter."""

393

394

@hookimpl

395

def start_test(self, parent_uuid, uuid, name, parameters, context):

396

"""Handle test start for framework integration."""

397

framework_context = context.get('framework')

398

if framework_context:

399

# Framework-specific test setup

400

framework_context.setup_test_environment()

401

402

@hookimpl

403

def stop_test(self, parent_uuid, uuid, name, context, exc_type, exc_val, exc_tb):

404

"""Handle test completion for framework integration."""

405

framework_context = context.get('framework')

406

if framework_context:

407

# Framework-specific cleanup

408

framework_context.cleanup_test_environment()

409

410

# Handle test failures

411

if exc_type:

412

print(f"Test {name} failed: {exc_val}")

413

414

# Register plugins

415

plugin_manager.register(CustomReporter())

416

plugin_manager.register(TestFrameworkAdapter())

417

```

418

419

### Advanced Plugin Patterns

420

421

#### Multi-Reporter Setup

422

423

```python

424

from allure_commons.logger import AllureFileLogger, AllureMemoryLogger

425

426

class DatabaseReporter:

427

"""Store results in database."""

428

429

@hookimpl

430

def report_result(self, result):

431

# Store in database

432

database.store_test_result({

433

'uuid': result.uuid,

434

'name': result.name,

435

'status': result.status,

436

'labels': [{'name': l.name, 'value': l.value} for l in result.labels]

437

})

438

439

# Register multiple reporters

440

file_logger = AllureFileLogger("/tmp/allure-results")

441

memory_logger = AllureMemoryLogger() # Store results in memory

442

db_reporter = DatabaseReporter()

443

444

plugin_manager.register(file_logger)

445

plugin_manager.register(memory_logger)

446

plugin_manager.register(db_reporter)

447

```

448

449

### Built-in Logger Classes

450

451

The library provides two built-in reporter implementations for common use cases.

452

453

```python { .api }

454

class AllureFileLogger:

455

"""

456

File-based logger that writes results to JSON files.

457

458

Writes test results, containers, and attachments to a specified directory

459

in the standard Allure JSON format.

460

"""

461

462

def __init__(self, report_dir, clean=False):

463

"""

464

Initialize file logger.

465

466

Parameters:

467

- report_dir (str): Directory to write result files

468

- clean (bool): Whether to clean existing files on initialization

469

"""

470

471

@hookimpl

472

def report_result(self, result):

473

"""Write test result to JSON file."""

474

475

@hookimpl

476

def report_container(self, container):

477

"""Write test container to JSON file."""

478

479

@hookimpl

480

def report_attached_file(self, source, file_name):

481

"""Copy attached file to results directory."""

482

483

@hookimpl

484

def report_attached_data(self, body, file_name):

485

"""Write attached data to file in results directory."""

486

487

class AllureMemoryLogger:

488

"""

489

Memory-based logger that stores results in memory for programmatic access.

490

491

Useful for testing, debugging, or custom result processing.

492

"""

493

494

def __init__(self):

495

"""Initialize memory logger with empty collections."""

496

497

# Attributes:

498

# - test_cases (list): List of test result dictionaries

499

# - test_containers (list): List of container dictionaries

500

# - attachments (dict): Dictionary mapping file names to content

501

502

@hookimpl

503

def report_result(self, result):

504

"""Store test result in memory."""

505

506

@hookimpl

507

def report_container(self, container):

508

"""Store test container in memory."""

509

510

@hookimpl

511

def report_attached_file(self, source, file_name):

512

"""Store file path reference in memory."""

513

514

@hookimpl

515

def report_attached_data(self, body, file_name):

516

"""Store attached data in memory."""

517

```

518

519

**Usage Examples:**

520

521

```python

522

from allure_commons.logger import AllureFileLogger, AllureMemoryLogger

523

524

# File-based logging

525

file_logger = AllureFileLogger("/tmp/allure-results", clean=True)

526

plugin_manager.register(file_logger)

527

528

# Memory-based logging for analysis

529

memory_logger = AllureMemoryLogger()

530

plugin_manager.register(memory_logger)

531

532

# After test execution, access results

533

print(f"Executed {len(memory_logger.test_cases)} tests")

534

print(f"Created {len(memory_logger.test_containers)} containers")

535

print(f"Generated {len(memory_logger.attachments)} attachments")

536

537

# Process results programmatically

538

for test_case in memory_logger.test_cases:

539

if test_case['status'] == 'failed':

540

print(f"Failed test: {test_case['name']}")

541

```

542

543

#### Conditional Hook Execution

544

545

```python

546

class ConditionalReporter:

547

"""Reporter that executes based on conditions."""

548

549

def __init__(self, enabled=True, filter_status=None):

550

self.enabled = enabled

551

self.filter_status = filter_status

552

553

@hookimpl

554

def report_result(self, result):

555

if not self.enabled:

556

return

557

558

if self.filter_status and result.status != self.filter_status:

559

return

560

561

# Process filtered results

562

self.process_result(result)

563

564

# Only report failed tests

565

failed_reporter = ConditionalReporter(filter_status='failed')

566

plugin_manager.register(failed_reporter)

567

```