TestNG | Report collection

You can use TestNG to collect reporting data using the standard TestNG listeners as well as the custom listeners.

Before continuing, review the following:

Thread Local Reporting Class

Reporting Manager

Copy
public class ReportingManager {
    private static ThreadLocal<Reporting> reporting = new ThreadLocal<Reporting>();

    public static Reporting getReporting() {
        return reporting.get();
    }

    public static void setReporting(Reporting report) {
        reporting.set(report);
    }
}

Reporting Factory

Copy
public class ReportingFactory {
    public static Reporting createInstance() {
        Reporting reporting = new Reporting();
        return reporting;
    }
}

Reporting Class

Copy
public class Reporting {    
    public HashMap<String, Object> reporting = new HashMap<String, Object>();
}

Abstract Class

In this class, you establish the ThreadLocal instance of reporting through setReporting() in the beforeMethod listener. From here, you can store results in the reporting Map by calling getReport().reporting.put("parameter","value");. Last, in afterMethod, you can take the collected reporting data and pass it wherever you wish: database, excel, splunk, and so on.

Copy
public abstract class ClassHelper {
    public RemoteWebDriver driver;
    
    public void setReporting() {
        Reporting reporting = ReportingFactory.createInstance();
        ReportingManager.setReporting(reporting);
    }

    // Returns the reporting class for use
    public Reporting getReport() {
        return ReportingManager.getReporting();
    }

    @BeforeMethod(alwaysRun = true)
    public void beforeMethod(Method method, String targetEnvironment, String network, String networkLatency) {
        setReporting();        
        getReport().setTestExecutionStart(System.currentTimeMillis());
        getReport().reporting.put("threadNo", Thread.currentThread().getName() + " " + Thread.currentThread().getId());        
    }

    @AfterMethod(alwaysRun = true)
    public void afterMethod(Method method, ITestResult testResult, ITestContext tcc) throws IOException {    
                getReport().reporting.put("executionID",
                        (String)driver.getCapabilities().getCapability("executionId"));
                getReport().reporting.put("reportKey",
                        (String)driver.getCapabilities().getCapability("reportKey"));                
                getReport().reporting.put("windTunnelReport",
                        (String)driver.getCapabilities().getCapability("windTunnelReportUrl"));

    /////////////////////////////////////////////////////////////////////
    // do something here with the reporting data
    /////////////////////////////////////////////////////////////////////
            
    }
}

Custom Listener Class

The following class uses the pass, skip, and fail listeners to gather the result of the test execution and pass it to a method that collects some additional reporting details. You can utilize reflection to recreate the classHelper object within the listener. This lets you access the driver used to gather capability information from your test.

Through the classHelper object, you can also initialize the reporting class in the event that a skip occurred (on skip beforeMethod isn't called). Last, at the end of the method, if Skip did occur, you can feed your report data to your database or wherever you want to store the data because afterMethod in the classHelper is not called.

Copy
public class TestListener implements ITestListener {
    @Override
    public void onTestStart(ITestResult result) { }    
    
    @Override
    public void onStart(ITestContext arg0) { }


    @Override
    public void onFinish(ITestContext arg0) { }    


    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) { }
    
    @Override
    public void onTestSuccess(ITestResult arg0) {
        setDetails("Pass", arg0);
    }

    @Override
    public void onTestFailure(ITestResult arg0) {
        setDetails("Fail", arg0);
    }

    @Override
    public void onTestSkipped(ITestResult arg0) {
        setDetails("Skip", arg0);
    }

    public void setDetails(String result, ITestResult testResult) {
        Object currentClass = testResult.getMethod().getInstance();
        ClassHelper classHelper = ((ClassHelper) currentClass);

        if (result.equalsIgnoreCase("Skip")) {
            classHelper.setReporting();
        }

        Reporting report = classHelper.getReport();

        report.reporting.put("testStatus", result);

        report.reporting.put("className", testResult.getMethod().getInstance().getClass().getName());

        try {
            report.reporting.put("hostName", InetAddress.getLocalHost().getHostName());
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if (!result.equalsIgnoreCase("Skip")) {
            report.reporting.put("model",(String) classHelper.driver.getCapabilities().getCapability("model"));
            report.reporting.put("device",(String) classHelper.driver.getCapabilities().getCapability("deviceName"));
        }

        //Upon failure check that throwable and stacktrace aren't null
        // before collecting the strack trace information otherwise
        // a null pointer exception will be thrown
        if (result.equalsIgnoreCase("Fail")) {
            if (testResult.getThrowable() != null)
                if (testResult.getThrowable().getStackTrace() != null) {
                    StringWriter sw = new StringWriter();
                    testResult.getThrowable().printStackTrace(new PrintWriter(sw));
                    report.reporting.put("stackTrace", sw.toString());
                }
        }

        report.reporting.put("testName", testResult.getTestContext().getName());
        report.reporting.put("methodName", testResult.getMethod().getMethodName());

        ///////////////////////////////////////////////////////////////////////
        // if (result.equalsIgnoreCase("Skip")) {
        //    do something here with the reporting info as afterMethod won't
        //    be called
        // }
        //////////////////////////////////////////////////////////////////////        
    }
}