Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One test watcher to report results of individual tests in JUnit Suite

So I have a suite, something like this:

@RunWith(Suite.class)
@Suite.SuiteClasses({TestClass1.class, TestClass2.class, TestClass3.class})
public class TestSuite {

    static List<ExtentTest> extentTestList = new ArrayList<>();

    @ClassRule
    public static ExtentWatcher extentWatcher = new ExtentWatcher() {
        @Override
        protected void starting(Description description) {
            extentTestList.addAll(extentWatcher.getTests());
        }
        @Override
        protected void finished(Description description) {
            extentWatcher.flushReports(extentTestList);
        }
    };
}

The above code works, but the problem is that it results in my Watcher reporting the results of the Suite, not the individual tests. Also, if a test fails, the suite still reports as passed. My Watcher is something like this:

public class ExtentWatcher extends TestWatcher {

    // A little set up to give the report a date in the file name
    private DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
    private Date date = new Date();
    private String fileDate = dateFormat.format(date);
    private String reportName = "./Test_Report_" + fileDate + ".html";
    public ExtentReports extent;
    ArrayList<ExtentTest> testList = new ArrayList<>();

    public ExtentWatcher() {
        extent = createReport();
    }

        // If test passed, watcher will record this with Extent Reports
        @Override
        protected void succeeded(Description description) {
            ExtentTest test = extent.startTest(description.getDisplayName());
            test.log(LogStatus.PASS, "Test Run Successful");
            testList.add(test);
        }

        // Likewise in case of failure
        @Override
        protected void failed(Throwable e, Description description) {
            ExtentTest test = extent.startTest(description.getDisplayName());
            test.log(LogStatus.FAIL, "Test Failure: " + e.toString());
            testList.add(test);
        }

    /**
     * These methods do the legwork - this file is based off of an example I found @ www.kieftsoft.nl/?p=81
     * Eventually we can add some screenshot logic here, but just a clean test report and version info will do
     */

    private ExtentReports createReport() {
        // Create the report - Extent just needs a little config
        ExtentReports extent = new ExtentReports(reportName, false);
        extent.config().reportName("Test Report: " + fileDate);
        return extent;
    }

    public void flushReports(List<ExtentTest> testList) {
        // This ends the test and then sends (flushes) everything to the html document
        for(ExtentTest test : testList) extent.endTest(test);
        extent.flush();
    }

    public List<ExtentTest> getTests() {
        return testList;
    }
}

This code works well annotated as @Rule for an individual test (with a report for each test individually, not desirable), but as per above, this isn't working on a Suite level and I'm really not sure how to make it work. I was thinking I could collect a list of all the tests, and in the suite, end the tests and flush them, which will allow ExtentReport to give me a report of all tests. However, I am unable to specifically get the individual test results - I will get one test back, with the displayName() = the Suite name.

How can I track the individual tests, then flush them when all have finished, and let the ExtentWatcher take care of the pass/fail on a test by test basis, instead of just once for the suite?

like image 932
SeeYaLater Automator Avatar asked Jan 25 '16 19:01

SeeYaLater Automator


1 Answers

I use a RunListener implementation, which logs the results in a file. First, I have a test runner class that gets called for each test suite:

public Result run(String suite) {
    Class<?> suiteClass = null;
    try {
      suiteClass = Class.forName(suite);
    } catch (ClassNotFoundException ex) {
      return null;
    }
    JUnitCore core = new JUnitCore();
    this.testRun = new TestRun(testLogging, suite);
    core.addListener(new TestRunListener(testLogging, testRun));
    Result result = core.run(suiteClass);
    return(result);
}

TestRun class is a custom class that simply counts the number of tests that have: started, passed, failed, ignored.

TestRunListener implements org.junit.runner.notification.RunListener, and tracks information about test status, based on the callbacks (e.g. testFailure(), testFinished(), etc.).

@Override
public void testFailure(Failure failure) throws Exception {
  classLogger.log(Level.CONFIG, failure.getMessage());
  classLogger.log(Level.CONFIG, failure.getTestHeader());
  classLogger.log(Level.CONFIG, failure.getTrace());
  classLogger.log(Level.CONFIG, failure.getDescription().getDisplayName());
  if (failure.getException() != null) {
    classLogger.log(Level.CONFIG, failure.getException().getMessage());
  }
  super.testFailure(failure);

  Description description = failure.getDescription();
  Test test = processTestResult(TestStatus.FAIL, description);
  if (test == null) {
    classLogger.log(Level.SEVERE, "TEST IS NULL:" + description.getDisplayName());
    return;
  }

  classLogger.log(Level.CONFIG, failure.getMessage()+ " " + description.getDisplayName());

  test.setFailureMessage(description.getDisplayName());
  test.setFailureException(failure.getException());
  LogRecord record = new LogRecord(Level.CONFIG, failure.getMessage());
  record.setParameters(new Object[] { test, failure });
  testFailuresLogger.log(record);
}
like image 101
Jim Newpower Avatar answered Sep 28 '22 13:09

Jim Newpower