As part of an experiment, I implemented a custom test reporting mechanism similar to ExtentReports.
- I used TestNG listeners (
ITestListener) to capture test execution events. - Each test result—success, failure, or skipped—is recorded in a
List<Map<String, String>> results. - The listener methods
onTestSuccess,onTestFailure, andonTestSkippedadd entries for each test case to this list. - After the entire test suite finishes, the
onFinishmethod is invoked automatically by TestNG. - In
onFinish, the accumulated results are written into a JSON report file (test-results.json) using Java’sFileWriter. - This JSON can later be used to generate HTML reports or for further visualization (charts, dashboards, etc.).
This approach allows you to have a lightweight, flexible, and customizable reporting solution without relying on external reporting libraries.
Example: JsonReporter Class in Java
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonReporter implements ITestListener {
private final List<Map<String, String>> results = new ArrayList<>();
// Called when a test passes
@Override
public void onTestSuccess(ITestResult result) {
recordResult(result, "PASS", null);
}
// Called when a test fails
@Override
public void onTestFailure(ITestResult result) {
recordResult(result, "FAIL", result.getThrowable() != null ? result.getThrowable().toString() : "");
}
// Called when a test is skipped
@Override
public void onTestSkipped(ITestResult result) {
recordResult(result, "SKIPPED", null);
}
// Helper method to record test result in the results list
private void recordResult(ITestResult result, String status, String error) {
Map<String, String> entry = new HashMap<>();
entry.put("testName", result.getName());
entry.put("status", status);
if (error != null) entry.put("error", error);
results.add(entry);
}
// Called once after all tests in the suite have finished
@Override
public void onFinish(ITestContext context) {
StringBuilder json = new StringBuilder("[\n");
for (int i = 0; i < results.size(); i++) {
Map<String, String> entry = results.get(i);
json.append(" {\n");
json.append(" \"testName\": \"").append(entry.get("testName")).append("\",\n");
json.append(" \"status\": \"").append(entry.get("status")).append("\"");
if (entry.containsKey("error")) {
json.append(",\n \"error\": \"").append(entry.get("error")).append("\"");
}
json.append("\n }");
if (i < results.size() - 1) json.append(",");
json.append("\n");
}
json.append("]\n");
try (FileWriter writer = new FileWriter("test-output/test-results.json")) {
writer.write(json.toString());
System.out.println("Results written to test-output/test-results.json");
} catch (Exception e) {
e.printStackTrace();
}
}
// Optional overrides (not required)
@Override public void onTestStart(ITestResult result) {}
@Override public void onStart(ITestContext context) {}
}
Add Listerner TestNG Suite file:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE suite SYSTEM “https://testng.org/testng-1.0.dtd”>
<suite name=”Suite”>
<listeners>
<listener class-name=”tests.JsonReporter”/>
</listeners>
<test thread-count=”5″ name=”Test”>
<classes>
<class name=”tests.LoginTest” />
<class name=”tests.SampleTest” />
</classes>
</test> <!– Test –>
</suite> <!– Suite –>
Once the JSON report (test-results.json) is generated, we can open reports.html to display the test results in a readable format.
- The HTML page reads
test-results.jsonusing thefetch()method. - The test results are inserted row by row into a table with the ID
resultsTable. - For better readability, the report also includes a pie chart that visualizes the distribution of PASS and FAIL tests.
- Green is used to represent passed tests.
- Red is used to represent failed tests.
- Display Reports Locally: Hosting HTML and JSON Files on Localhost (Required for Fetch to Read Files)
This approach allows a quick visual summary of test outcomes while maintaining a detailed tabular report for individual test cases.
HTML example with JavaScript
<!DOCTYPE html>
<html>
<head>
<title>Test Dashboard</title>
<script src=”https://cdn.jsdelivr.net/npm/chart.js”></script>
</head>
<body>
<h2>Selenium Test Results</h2>
<table border=”1″ id=”resultsTable”>
<tr><th>Test Name</th><th>Status</th><th>Error</th></tr>
</table>
<canvas id=”statusChart” width=”100″ height=”100″></canvas>
<script>
fetch(‘test-results.json’)
.then(res => res.json())
.then(data => {
let pass = 0, fail = 0;
const table = document.getElementById(‘resultsTable’);
data.forEach(t => {
const row = table.insertRow();
row.insertCell(0).innerText = t.testName;
row.insertCell(1).innerText = t.status;
row.insertCell(2).innerText = t.error || ”;
if(t.status === ‘PASS’) pass++; else fail++;
});
// Chart
new Chart(document.getElementById(‘statusChart’), {
type: ‘pie’,
data: {
labels: [‘PASS’, ‘FAIL’],
datasets: [{
data: [pass, fail],
backgroundColor: [‘green’, ‘red’]
}]
}
});
});
</script>
</body>
</html>