Answer
IReporter in TestNG
IReporter is called once after the entire suite finishes. It receives the complete test results and lets you generate any output format — CSV, JSON, email, custom HTML.
Implement IReporter
Java
import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.xml.XmlSuite;
import java.io.*;
import java.util.*;
public class CustomCSVReporter implements IReporter {
@Override
public void generateReport(List<XmlSuite> xmlSuites,
List<ISuite> suites,
String outputDirectory) {
StringBuilder sb = new StringBuilder();
sb.append("TestName,Status,Duration(ms),ClassName\n");
for (ISuite suite : suites) {
Map<String, ISuiteResult> results = suite.getResults();
for (ISuiteResult suiteResult : results.values()) {
ITestContext context = suiteResult.getTestContext();
// Passed tests
context.getPassedTests().getAllResults().forEach(result -> {
sb.append(result.getName()).append(",PASS,")
.append(result.getEndMillis() - result.getStartMillis()).append(",")
.append(result.getTestClass().getName()).append("\n");
});
// Failed tests
context.getFailedTests().getAllResults().forEach(result -> {
sb.append(result.getName()).append(",FAIL,")
.append(result.getEndMillis() - result.getStartMillis()).append(",")
.append(result.getTestClass().getName()).append("\n");
});
// Skipped tests
context.getSkippedTests().getAllResults().forEach(result -> {
sb.append(result.getName()).append(",SKIP,0,")
.append(result.getTestClass().getName()).append("\n");
});
}
}
// Write to file
try {
String filePath = outputDirectory + "/custom-report.csv";
new File(outputDirectory).mkdirs();
try (FileWriter fw = new FileWriter(filePath)) {
fw.write(sb.toString());
}
System.out.println("Custom CSV report generated: " + filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Register in testng.xml
XML
<suite name="Suite">
<listeners>
<listener class-name="reporters.CustomCSVReporter"/>
</listeners>
<test name="Tests">
<classes><class name="tests.LoginTest"/></classes>
</test>
</suite>
Generate Summary Statistics
Java
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outDir) {
int passed = 0, failed = 0, skipped = 0;
for (ISuite suite : suites) {
for (ISuiteResult result : suite.getResults().values()) {
ITestContext ctx = result.getTestContext();
passed += ctx.getPassedTests().size();
failed += ctx.getFailedTests().size();
skipped += ctx.getSkippedTests().size();
}
}
int total = passed + failed + skipped;
double passRate = total > 0 ? (double) passed / total * 100 : 0;
System.out.println("========= Test Summary =========");
System.out.printf("Total: %d%n", total);
System.out.printf("Passed: %d (%.1f%%)%n", passed, passRate);
System.out.printf("Failed: %d%n", failed);
System.out.printf("Skipped: %d%n", skipped);
System.out.println("================================");
}
IReporter vs ITestListener
| Interface | When Called | Scope | Use For |
|---|---|---|---|
ITestListener | During execution (per test) | Real-time | Live logging, ExtentReports |
IReporter | After entire suite | Post-execution | Final CSV/JSON/email reports |
