Sunday, 23 April 2017

Junit Listener for Unit Test cases - Its implementation with simple HTML Reporting

          Many of us already know that JUnit is a unit testing frame work and it is widely used by the developers to write the unit test cases. But most of the QAs would prefer to go with TestNG because of the more features offered by it. The main features of the TestNG are its Listeners and its own reporting. What if we could implement the listener with JUnit? What if we could implement the reporting with the help of that Listener? This makes the JUnit user's life easier.

Here is my attempt to do so. In this post We are going to learn about implementing the JUnit Listener and generating the HTML report with the help of that listener.

First of all, in your project, create a class called HTMLReport and paste the below code.

public class HTMLReport {
  public static String reportFileName = "unknown";
  public static int sNo=0;
  public static void createFolder(String strDirectoy) {
    boolean success = (new File(strDirectoy)).mkdirs();
    if (success) {
    }
  }
  public static void createHTMLTemplate(String environment, String strClassName) throws IOException {
    File tempFolder = new File("./junit");
    createFolder(tempFolder.getCanonicalPath().toString());
    reportFileName=tempFolder.getCanonicalPath().toString()+"//"+strClassName+"UnitTestReport.html";
    File file = new File(reportFileName);
    boolean blnCreated = false;
    try {
      blnCreated = file.createNewFile();
    } catch (IOException ioe) {
      System.out.println("Error while creating a new empty file :" + ioe);
    }
    System.out.println("Was file " + file.getPath() + " created ? : " + blnCreated);
    BufferedWriter writer = null;
    try {
      BufferedWriter bw = new BufferedWriter(new FileWriter(file));
      bw.write("<html><body>");
      bw.write("<title>Results</title>");
      bw.write(
          "</title><BODY bgcolor=White><TABLE BORDER=0 CELLPADDING=3 CELLSPACING=1 WIDTH=100%>");
      bw.write(
          "<table id=check1><TR COLS=2><TD WIDTH=20%></TD><TD WIDTH=10% align=Right><TD WIDTH=10% align=Right></TD></TABLE>");
      bw.write(
          "<br/><table border=\"0\" width=\"100%\" height=\"45\" border-spacing: 0px bgcolor=\"#728139\" style=\"font-family:Copperplate Gothic Bold; font-size:25px; color: #fffbbf;\"><tr height=\"21\"><td valign=\"center\" align=\"center\" width=\"80%\"><p align=\"center\" style=\"font-family: Copperplate Gothic Bold\"> Your Own Project Unit Test Execution Report</p></td></tr></table><br/><br/>");

      bw.write(
          "<table><TR COLS=4><TD BGCOLOR=#D8BFD8 WIDTH=5%><FONT FACE=VERDANA COLOR=Black SIZE=2><B> Project Name <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=#00008B SIZE=2><B> Your Own Project <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD  WIDTH=10%></TD><TD  WIDTH=10%></TD></TR>");
      bw.write(
          "<TR COLS=1><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=Black SIZE=2><B> Enviornment <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=#00008B SIZE=2><B> "
              + environment
              + " <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD  WIDTH=10%></TD><TD  WIDTH=10%></TD></TR>");
      bw.write(
          "<TR COLS=1><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=Black SIZE=2><B> User <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=#00008B SIZE=2><B>"+ System.getProperty("user.name") +"<DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD  WIDTH=10%></TD><TD  WIDTH=10%></TD></TR>");
      bw.write(
          "<TR COLS=1><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=Black SIZE=2><B> Date&Time <DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD BGCOLOR=#D8BFD8 WIDTH=10%><FONT FACE=VERDANA COLOR=#00008B SIZE=2><B>"+ Utils.getCurrentDateTime("dd-MMM-yyyy hh:mm:ss.SSS") +"<DIV ALIGN=RIGHT></DIV></B></FONT></TD><TD  WIDTH=10%></TD><TD  WIDTH=10%></TR></TD></table>");

      bw.write("<table width=\"100%\"><TABLEBORDER=0 BGCOLOR=YELLOW CELLPADDING=3 CELLSPACING=1 WIDTH=20%>");
      bw.write(
          "<TR COLS=5><TD BGCOLOR=#993300 WIDTH=3%><FONT FACE=VERDANA COLOR=White SIZE=1><center><B> SNo </B><center></FONT></TD>");
      bw.write(
          "<TD BGCOLOR=#993300 WIDTH=30%><FONT FACE=VERDANA COLOR=White SIZE=1.5><B> TestCaseName <B/></FONT></TD>");
      bw.write(
          "<TD BGCOLOR=#993300 WIDTH=10%><FONT FACE=VERDANA COLOR=White SIZE=1.5><B> Status </B></FONT></TD>");
      bw.write(
          "<TD BGCOLOR=#993300 WIDTH=60%><FONT FACE=VERDANA COLOR=White SIZE=1.5><B> Failure Details </B></FONT></TD>");
      bw.close();
    } catch (IOException e) {
      System.out.println("Exception Occured" + e.getMessage());
    } finally {
      try {
        if (writer != null)
          writer.close();
      } catch (IOException e) {
      }
    }
  }

  public static void reportPass(String strTestCaseName, String strStatus ) {
    BufferedWriter bw = null;
    File file = new File(reportFileName);
    
    try {
      bw = new BufferedWriter(new FileWriter(file, true));
      bw.write(
          "<TR COLS=5><TD BGCOLOR=#FFE4C4 WIDTH=3%><FONT FACE=VERDANA COLOR=GREEN SIZE=1><center>"
              + sNo + "</center></FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=30%><FONT FACE=VERDANA COLOR=GREEN SIZE=1>" + strTestCaseName
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=10%><FONT FACE=VERDANA COLOR=GREEN SIZE=1>" + strStatus
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=60%><FONT FACE=VERDANA COLOR=GREEN SIZE=1>" + ""
          + "</FONT></TD>");
      bw.close();
    } catch (IOException e) {

    } finally {
      try {
        if (bw != null)
          bw.close();
      } catch (IOException e) {
      }
    }
  }

  public static void reportFail(String strTestCaseName, String strStatus, String strStackTrace) {
    BufferedWriter bw = null;
    File file = new File(reportFileName);
    try {
      bw = new BufferedWriter(new FileWriter(file, true));
      bw.write(
          "<TR COLS=5><TD BGCOLOR=#FFE4C4 WIDTH=3%><FONT FACE=VERDANA COLOR=RED SIZE=1><center>"
              + sNo + "</center></FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=30%><FONT FACE=VERDANA COLOR=RED SIZE=1>" + strTestCaseName
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=10%><FONT FACE=VERDANA COLOR=RED SIZE=1>" + strStatus
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=60%><FONT FACE=VERDANA COLOR=RED SIZE=1>" + strStackTrace
          + "</FONT></TD>");
      bw.close();
    } catch (IOException e) {

    } finally {
      try {
        if (bw != null)
          bw.close();
      } catch (IOException e) {
      }
    }
  }
  
  public static void reportIgnored(String strTestCaseName, String strStatus) {
    BufferedWriter bw = null;
    File file = new File(reportFileName);
    String strStackTrace="";
    try {
      bw = new BufferedWriter(new FileWriter(file, true));
      bw.write(
          "<TR COLS=5><TD BGCOLOR=#FFE4C4 WIDTH=3%><FONT FACE=VERDANA COLOR=RED SIZE=1><center>"
              + sNo + "</center></FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=30%><FONT FACE=VERDANA COLOR=BLUE SIZE=1>" + strTestCaseName
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=10%><FONT FACE=VERDANA COLOR=BLUE SIZE=1>" + strStatus
          + "</FONT></TD>");
      bw.write("<TD BGCOLOR=#FFE4C4 WIDTH=60%><FONT FACE=VERDANA COLOR=BLUE SIZE=1>" + strStackTrace
          + "</FONT></TD>");
      bw.close();
    } catch (IOException e) {

    } finally {
      try {
        if (bw != null)
          bw.close();
      } catch (IOException e) {
      }
    }
  }
}
    In the above class, We are implementing the reporting part by using Java. We use the plain HTML tags to generate the report. This code creates a folder called "junit" in your project and report would be placed under it. You can modify these HTML tags and the text according to your project needs.

     Next, we are going to implement the JUnit listener by extending the RunListener class provided by JUnit. RunListener is a class, which hears all the events performed in your Unit test cases using JUnit. You can find more about RunListener below:

   Again here, I have implemented this custom listener class by considering report in mind. You can customize this class according to your project needs. Create a class called JUnitExecutionListener and paste the below code.

public class JUnitExecutionListener extends RunListener {
  public DomainUtils domainUtils = new DomainUtils();
  public static boolean blnFailed=false;
  
  @Override
  public void testRunStarted(Description description) throws Exception {
    HTMLReport.createHTMLTemplate("Your own env",description.getClassName().split("\\.")[description.getClassName().split("\\.").length-1]);
    System.out.println("Number of tests to execute: " + description.getMethodName());
  }

  @Override
  public void testRunFinished(Result result) throws Exception {
    System.out.println(result.getFailureCount());
    System.out.println("Number of tests executed: " + result.getRunCount());
  }

  @Override
  public void testStarted(Description description) throws Exception {
    HTMLReport.sNo=HTMLReport.sNo+1;
    blnFailed=false;
    System.out.println("Starting: " + description.getMethodName());
  }

  @Override
  public void testFinished(Description description) throws Exception {
    if(!blnFailed){
      HTMLReport.reportPass(description.getMethodName(), "Passed");
    }
    System.out.println("Finished: " + description.getMethodName());
  }

  @Override
  public void testFailure(Failure failure) throws Exception {
    blnFailed=true;
    HTMLReport.reportFail(failure.getDescription().getMethodName(), "failed",
     failure.getTrace());
    System.out.println("Failed: " + failure.getDescription().getMethodName());
  }

  @Override
  public void testAssumptionFailure(Failure failure) {
    System.out.println("Failed: " + failure.getDescription().getMethodName());
  }

  @Override
  public void testIgnored(Description description) throws Exception {
    HTMLReport.sNo=HTMLReport.sNo+1;
    HTMLReport.reportIgnored(description.getMethodName(), "Skipped");
    System.out.println("Ignored: " + description.getMethodName());
  }

}

      Now, We are going to implement the custom runner class. Then we use this runner class with the @RunWith annotation which will register our JUnit Listener to the test case. That code is as below

public class MyRunner extends BlockJUnit4ClassRunner {

    public MyRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    public void run (RunNotifier notifier){
        //Add Listener. This will register our JUnit Listener.
        notifier.addListener(new JUnitExecutionListener());

        //Get each test notifier
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        try {
            //In order capture testRunStarted method
            //at the very beginning of the test run, we will add below code.
            //Invoke here the run testRunStarted() method
            notifier.fireTestRunStarted(getDescription());
            Statement statement = classBlock(notifier);
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            testNotifier.fireTestIgnored();
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }
}

With this, we are done with the whole setup. Now write a class with sample Unit test cases as below:

@RunWith(MyRunner.class)
public class JunitSample {
  public DomainUtils domainUtils= new DomainUtils();
    @Test
    public void testListener(){

    }

    @Test
    public void testFalseAssertion(){
        assertTrue(false);
    }

    @Ignore
    @Test
    public void testIgnore(){

    }

    @Test
    public void testException(){
        throw new RuntimeException();
    }
}

         If you observe in the above class, I have used @Runwith annotation with my custom runner class. Now all the test cases would be executed via my runner implementation. If you run this class, you will get a report as below.

         This implementation would be very helpful, particularly when you have many cases to run. While executing these many cases, it is hard to track all the test cases. This implementation is also purely Java and simple HTML solution. 

        You can even use these reports with CI tools such as Jenkins. Jenkins has a plugin called htmlpublisher,  through which you can configure the reports. Below link would be useful if you want to configure that.

No comments:

Post a Comment