or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdassertions.mdcollections-utilities.mdindex.mdlisteners-hooks.mdtest-execution.mdxml-configuration.md

listeners-hooks.mddocs/

0

# Event Listeners and Hooks

1

2

TestNG's comprehensive event system provides hooks for customizing test behavior through listener interfaces. This enables custom reporting, logging, test modification, and integration with external systems during test execution.

3

4

## Capabilities

5

6

### Test Execution Listeners

7

8

Core listener interfaces for monitoring test execution events and implementing custom behavior during test lifecycle.

9

10

```java { .api }

11

/**

12

* Primary listener interface for test execution events

13

*/

14

public interface ITestListener {

15

16

// Test method events

17

default void onTestStart(ITestResult result) {}

18

default void onTestSuccess(ITestResult result) {}

19

default void onTestFailure(ITestResult result) {}

20

default void onTestSkipped(ITestResult result) {}

21

default void onTestFailedButWithinSuccessPercentage(ITestResult result) {}

22

default void onTestFailedWithTimeout(ITestResult result) {}

23

24

// Test context events

25

default void onStart(ITestContext context) {}

26

default void onFinish(ITestContext context) {}

27

}

28

29

/**

30

* Suite-level listener for suite execution events

31

*/

32

public interface ISuiteListener {

33

default void onStart(ISuite suite) {}

34

default void onFinish(ISuite suite) {}

35

}

36

37

/**

38

* Configuration method listener for setup/teardown events

39

*/

40

public interface IConfigurationListener {

41

default void onConfigurationSuccess(ITestResult itr) {}

42

default void onConfigurationFailure(ITestResult itr) {}

43

default void onConfigurationSkip(ITestResult itr) {}

44

default void beforeConfiguration(ITestResult tr) {}

45

}

46

47

/**

48

* Class-level listener for test class events

49

*/

50

public interface IClassListener {

51

default void onBeforeClass(ITestClass testClass) {}

52

default void onAfterClass(ITestClass testClass) {}

53

}

54

```

55

56

**Usage Examples:**

57

58

```java

59

import org.testng.ITestListener;

60

import org.testng.ITestResult;

61

import org.testng.ITestContext;

62

import org.testng.ISuiteListener;

63

import org.testng.ISuite;

64

65

public class CustomTestListener implements ITestListener {

66

67

@Override

68

public void onStart(ITestContext context) {

69

System.out.println("Test execution started: " + context.getName());

70

System.out.println("Included groups: " + Arrays.toString(context.getIncludedGroups()));

71

System.out.println("Excluded groups: " + Arrays.toString(context.getExcludedGroups()));

72

}

73

74

@Override

75

public void onTestStart(ITestResult result) {

76

System.out.println("Starting test: " + result.getMethod().getMethodName());

77

System.out.println("Test class: " + result.getTestClass().getName());

78

System.out.println("Groups: " + Arrays.toString(result.getMethod().getGroups()));

79

}

80

81

@Override

82

public void onTestSuccess(ITestResult result) {

83

long duration = result.getEndMillis() - result.getStartMillis();

84

System.out.println("✓ Test passed: " + result.getMethod().getMethodName()

85

+ " (" + duration + "ms)");

86

}

87

88

@Override

89

public void onTestFailure(ITestResult result) {

90

System.out.println("✗ Test failed: " + result.getMethod().getMethodName());

91

System.out.println("Parameters: " + Arrays.toString(result.getParameters()));

92

93

Throwable throwable = result.getThrowable();

94

if (throwable != null) {

95

System.out.println("Error: " + throwable.getMessage());

96

throwable.printStackTrace();

97

}

98

99

// Take screenshot, log additional info, etc.

100

takeScreenshotOnFailure(result);

101

}

102

103

@Override

104

public void onTestSkipped(ITestResult result) {

105

System.out.println("⊘ Test skipped: " + result.getMethod().getMethodName());

106

107

Throwable throwable = result.getThrowable();

108

if (throwable != null) {

109

System.out.println("Reason: " + throwable.getMessage());

110

}

111

}

112

113

@Override

114

public void onFinish(ITestContext context) {

115

System.out.println("Test execution finished: " + context.getName());

116

System.out.println("Passed: " + context.getPassedTests().size());

117

System.out.println("Failed: " + context.getFailedTests().size());

118

System.out.println("Skipped: " + context.getSkippedTests().size());

119

120

// Generate custom reports

121

generateCustomReport(context);

122

}

123

124

private void takeScreenshotOnFailure(ITestResult result) {

125

// Screenshot logic for web tests

126

if (result.getInstance() instanceof WebTest) {

127

WebTest webTest = (WebTest) result.getInstance();

128

webTest.takeScreenshot(result.getMethod().getMethodName());

129

}

130

}

131

132

private void generateCustomReport(ITestContext context) {

133

// Custom reporting logic

134

System.out.println("Generating custom test report...");

135

}

136

}

137

138

public class CustomSuiteListener implements ISuiteListener {

139

140

@Override

141

public void onStart(ISuite suite) {

142

System.out.println("Suite started: " + suite.getName());

143

System.out.println("Output directory: " + suite.getOutputDirectory());

144

145

// Initialize suite-level resources

146

initializeDatabaseConnection();

147

setupTestEnvironment();

148

}

149

150

@Override

151

public void onFinish(ISuite suite) {

152

System.out.println("Suite finished: " + suite.getName());

153

154

// Collect overall statistics

155

Map<String, ISuiteResult> results = suite.getResults();

156

int totalPassed = 0, totalFailed = 0, totalSkipped = 0;

157

158

for (ISuiteResult result : results.values()) {

159

ITestContext context = result.getTestContext();

160

totalPassed += context.getPassedTests().size();

161

totalFailed += context.getFailedTests().size();

162

totalSkipped += context.getSkippedTests().size();

163

}

164

165

System.out.println("Suite Summary:");

166

System.out.println(" Total Passed: " + totalPassed);

167

System.out.println(" Total Failed: " + totalFailed);

168

System.out.println(" Total Skipped: " + totalSkipped);

169

170

// Cleanup suite-level resources

171

cleanupDatabaseConnection();

172

teardownTestEnvironment();

173

}

174

175

private void initializeDatabaseConnection() {

176

// Database setup logic

177

}

178

179

private void setupTestEnvironment() {

180

// Environment setup logic

181

}

182

183

private void cleanupDatabaseConnection() {

184

// Database cleanup logic

185

}

186

187

private void teardownTestEnvironment() {

188

// Environment teardown logic

189

}

190

}

191

```

192

193

### Custom Reporting Listeners

194

195

Specialized listeners for generating custom reports and integrating with external reporting systems.

196

197

```java { .api }

198

/**

199

* Interface for custom test reporters

200

*/

201

public interface IReporter {

202

void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory);

203

}

204

205

/**

206

* Interface for invoked method tracking

207

*/

208

public interface IInvokedMethodListener {

209

default void beforeInvocation(IInvokedMethod method, ITestResult testResult) {}

210

default void afterInvocation(IInvokedMethod method, ITestResult testResult) {}

211

default void beforeInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {}

212

default void afterInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {}

213

}

214

215

/**

216

* Interface for execution-level events

217

*/

218

public interface IExecutionListener {

219

default void onExecutionStart() {}

220

default void onExecutionFinish() {}

221

}

222

```

223

224

**Usage Examples:**

225

226

```java

227

public class CustomReporter implements IReporter {

228

229

@Override

230

public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {

231

System.out.println("Generating custom HTML report...");

232

233

try {

234

// Create custom HTML report

235

String reportPath = outputDirectory + "/custom-report.html";

236

PrintWriter writer = new PrintWriter(new FileWriter(reportPath));

237

238

writer.println("<html><head><title>Test Report</title></head><body>");

239

writer.println("<h1>Test Execution Report</h1>");

240

241

for (ISuite suite : suites) {

242

writer.println("<h2>Suite: " + suite.getName() + "</h2>");

243

244

Map<String, ISuiteResult> results = suite.getResults();

245

for (ISuiteResult result : results.values()) {

246

ITestContext context = result.getTestContext();

247

248

writer.println("<h3>Test: " + context.getName() + "</h3>");

249

writer.println("<p>Duration: " +

250

(context.getEndDate().getTime() - context.getStartDate().getTime()) + "ms</p>");

251

252

// Passed tests

253

writer.println("<h4>Passed Tests (" + context.getPassedTests().size() + ")</h4>");

254

writer.println("<ul>");

255

for (ITestResult testResult : context.getPassedTests().getAllResults()) {

256

writer.println("<li style='color: green;'>" +

257

testResult.getMethod().getMethodName() + "</li>");

258

}

259

writer.println("</ul>");

260

261

// Failed tests

262

writer.println("<h4>Failed Tests (" + context.getFailedTests().size() + ")</h4>");

263

writer.println("<ul>");

264

for (ITestResult testResult : context.getFailedTests().getAllResults()) {

265

writer.println("<li style='color: red;'>" +

266

testResult.getMethod().getMethodName());

267

if (testResult.getThrowable() != null) {

268

writer.println("<br/>Error: " + testResult.getThrowable().getMessage());

269

}

270

writer.println("</li>");

271

}

272

writer.println("</ul>");

273

}

274

}

275

276

writer.println("</body></html>");

277

writer.close();

278

279

System.out.println("Custom report generated: " + reportPath);

280

281

} catch (IOException e) {

282

System.err.println("Failed to generate custom report: " + e.getMessage());

283

}

284

}

285

}

286

287

public class MethodInvocationListener implements IInvokedMethodListener {

288

289

@Override

290

public void beforeInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {

291

String methodName = method.getTestMethod().getMethodName();

292

String className = method.getTestMethod().getTestClass().getName();

293

294

System.out.println("Before: " + className + "." + methodName);

295

296

if (method.isTestMethod()) {

297

System.out.println(" Type: Test Method");

298

System.out.println(" Groups: " + Arrays.toString(method.getTestMethod().getGroups()));

299

} else if (method.isConfigurationMethod()) {

300

System.out.println(" Type: Configuration Method");

301

}

302

303

// Log method parameters

304

Object[] parameters = testResult.getParameters();

305

if (parameters != null && parameters.length > 0) {

306

System.out.println(" Parameters: " + Arrays.toString(parameters));

307

}

308

}

309

310

@Override

311

public void afterInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {

312

String methodName = method.getTestMethod().getMethodName();

313

long duration = testResult.getEndMillis() - testResult.getStartMillis();

314

315

System.out.println("After: " + methodName + " (" + duration + "ms)");

316

System.out.println(" Status: " + getStatusName(testResult.getStatus()));

317

318

if (testResult.getThrowable() != null) {

319

System.out.println(" Exception: " + testResult.getThrowable().getClass().getSimpleName());

320

}

321

}

322

323

private String getStatusName(int status) {

324

switch (status) {

325

case ITestResult.SUCCESS: return "PASSED";

326

case ITestResult.FAILURE: return "FAILED";

327

case ITestResult.SKIP: return "SKIPPED";

328

case ITestResult.SUCCESS_PERCENTAGE_FAILURE: return "SUCCESS_PERCENTAGE_FAILURE";

329

default: return "UNKNOWN";

330

}

331

}

332

}

333

```

334

335

### Test Transformation and Interception

336

337

Advanced listener interfaces for modifying test behavior, annotations, and execution flow.

338

339

```java { .api }

340

/**

341

* Interface for transforming annotations at runtime

342

*/

343

public interface IAnnotationTransformer {

344

default void transform(ITestAnnotation annotation, Class testClass,

345

Constructor testConstructor, Method testMethod) {}

346

default void transform(IConfigurationAnnotation annotation, Class testClass,

347

Constructor testConstructor, Method testMethod) {}

348

default void transform(IDataProviderAnnotation annotation, Method method) {}

349

default void transform(IFactoryAnnotation annotation, Method method) {}

350

}

351

352

/**

353

* Interface for method interception and modification

354

*/

355

public interface IMethodInterceptor {

356

List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context);

357

}

358

359

/**

360

* Interface for hookable test methods

361

*/

362

public interface IHookable {

363

void run(IHookCallBack callBack, ITestResult testResult);

364

}

365

366

/**

367

* Interface for configurable configuration methods

368

*/

369

public interface IConfigurable {

370

void run(IConfigureCallBack callBack, ITestResult testResult);

371

}

372

```

373

374

**Usage Examples:**

375

376

```java

377

public class AnnotationTransformer implements IAnnotationTransformer {

378

379

@Override

380

public void transform(ITestAnnotation annotation, Class testClass,

381

Constructor testConstructor, Method testMethod) {

382

383

// Modify test timeout based on method name

384

if (testMethod != null && testMethod.getName().contains("Slow")) {

385

annotation.setTimeOut(30000); // 30 seconds for slow tests

386

}

387

388

// Add groups based on class name

389

if (testClass != null && testClass.getName().contains("Integration")) {

390

String[] currentGroups = annotation.getGroups();

391

String[] newGroups = Arrays.copyOf(currentGroups, currentGroups.length + 1);

392

newGroups[currentGroups.length] = "integration";

393

annotation.setGroups(newGroups);

394

}

395

396

// Enable retry for specific tests

397

if (testMethod != null && testMethod.isAnnotationPresent(Flaky.class)) {

398

annotation.setRetryAnalyzer(FlakyTestRetryAnalyzer.class);

399

}

400

}

401

402

@Override

403

public void transform(IDataProviderAnnotation annotation, Method method) {

404

// Make all data providers parallel by default

405

annotation.setParallel(true);

406

}

407

}

408

409

public class CustomMethodInterceptor implements IMethodInterceptor {

410

411

@Override

412

public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {

413

System.out.println("Intercepting " + methods.size() + " methods");

414

415

List<IMethodInstance> result = new ArrayList<>();

416

417

for (IMethodInstance method : methods) {

418

ITestNGMethod testMethod = method.getMethod();

419

420

// Skip methods marked with @Ignore annotation

421

if (testMethod.getConstructorOrMethod().getMethod().isAnnotationPresent(Ignore.class)) {

422

System.out.println("Skipping ignored method: " + testMethod.getMethodName());

423

continue;

424

}

425

426

// Run only smoke tests in CI environment

427

if (System.getProperty("CI") != null) {

428

String[] groups = testMethod.getGroups();

429

if (!Arrays.asList(groups).contains("smoke")) {

430

System.out.println("Skipping non-smoke test in CI: " + testMethod.getMethodName());

431

continue;

432

}

433

}

434

435

result.add(method);

436

}

437

438

// Sort methods by priority

439

result.sort((m1, m2) -> {

440

int priority1 = m1.getMethod().getPriority();

441

int priority2 = m2.getMethod().getPriority();

442

return Integer.compare(priority1, priority2);

443

});

444

445

System.out.println("Filtered to " + result.size() + " methods");

446

return result;

447

}

448

}

449

450

public class HookableTest implements IHookable {

451

452

@Override

453

public void run(IHookCallBack callBack, ITestResult testResult) {

454

System.out.println("Before test execution hook");

455

456

// Custom setup before test

457

setupTestData();

458

459

try {

460

// Execute the actual test

461

callBack.runTestMethod(testResult);

462

463

// Custom validation after test

464

validateTestResults();

465

466

} catch (Exception e) {

467

System.out.println("Test execution failed: " + e.getMessage());

468

throw e;

469

} finally {

470

// Custom cleanup after test

471

System.out.println("After test execution hook");

472

cleanupTestData();

473

}

474

}

475

476

@Test

477

public void hookableTestMethod() {

478

System.out.println("Executing hookable test method");

479

Assert.assertTrue(true);

480

}

481

482

private void setupTestData() {

483

System.out.println("Setting up test data in hook");

484

}

485

486

private void validateTestResults() {

487

System.out.println("Validating test results in hook");

488

}

489

490

private void cleanupTestData() {

491

System.out.println("Cleaning up test data in hook");

492

}

493

}

494

```

495

496

### Data Provider Listeners

497

498

Specialized listeners for data provider events and error handling.

499

500

```java { .api }

501

/**

502

* Listener for data provider events

503

*/

504

public interface IDataProviderListener {

505

default void beforeDataProviderExecution(IDataProviderMethod dataProviderMethod,

506

ITestNGMethod method, ITestContext iTestContext) {}

507

default void afterDataProviderExecution(IDataProviderMethod dataProviderMethod,

508

ITestNGMethod method, ITestContext iTestContext) {}

509

}

510

511

/**

512

* Interface for retry analyzers

513

*/

514

public interface IRetryAnalyzer {

515

boolean retry(ITestResult result);

516

}

517

518

/**

519

* Interface for data provider retry logic

520

*/

521

public interface IRetryDataProvider {

522

boolean retry(IDataProviderMethod dataProviderMethod);

523

}

524

```

525

526

**Usage Examples:**

527

528

```java

529

public class DataProviderListener implements IDataProviderListener {

530

531

@Override

532

public void beforeDataProviderExecution(IDataProviderMethod dataProviderMethod,

533

ITestNGMethod method, ITestContext context) {

534

System.out.println("Executing data provider: " + dataProviderMethod.getName() +

535

" for test: " + method.getMethodName());

536

}

537

538

@Override

539

public void afterDataProviderExecution(IDataProviderMethod dataProviderMethod,

540

ITestNGMethod method, ITestContext context) {

541

System.out.println("Data provider completed: " + dataProviderMethod.getName());

542

}

543

}

544

545

public class FlakyTestRetryAnalyzer implements IRetryAnalyzer {

546

private int retryCount = 0;

547

private static final int MAX_RETRY_COUNT = 3;

548

549

@Override

550

public boolean retry(ITestResult result) {

551

if (retryCount < MAX_RETRY_COUNT) {

552

retryCount++;

553

System.out.println("Retrying test: " + result.getMethod().getMethodName() +

554

" (attempt " + retryCount + " of " + MAX_RETRY_COUNT + ")");

555

return true;

556

}

557

return false;

558

}

559

}

560

```

561

562

## Types

563

564

```java { .api }

565

// Listener factory interface

566

public interface ITestNGListenerFactory {

567

ITestNGListener createListener(Class<? extends ITestNGListener> listenerClass);

568

}

569

570

// Invoked method interface

571

public interface IInvokedMethod {

572

ITestNGMethod getTestMethod();

573

Object getInstance();

574

long getDate();

575

boolean isTestMethod();

576

boolean isConfigurationMethod();

577

}

578

579

// Hook callback interfaces

580

public interface IHookCallBack {

581

void runTestMethod(ITestResult testResult);

582

Object[] getParameters();

583

}

584

585

public interface IConfigureCallBack {

586

void runConfigurationMethod(ITestResult testResult);

587

Object[] getParameters();

588

}

589

590

// Method instance for interceptors

591

public interface IMethodInstance {

592

ITestNGMethod getMethod();

593

Object getInstance();

594

}

595

596

// Data provider method interface

597

public interface IDataProviderMethod {

598

String getName();

599

Method getMethod();

600

Object getInstance();

601

}

602

603

// Custom annotations for examples

604

@Retention(RetentionPolicy.RUNTIME)

605

@Target(ElementType.METHOD)

606

public @interface Flaky {

607

}

608

609

@Retention(RetentionPolicy.RUNTIME)

610

@Target(ElementType.METHOD)

611

public @interface Ignore {

612

}

613

```