Selenium WebDriver – Real Time Test Execution Results Using Elasticsearch & Kibana

Overview:

Running automated regression on a daily basis as part of daily build is inevitable nowadays! It is cool to find & report the issues as soon as they are introduced. But it is very painful to maintain hundreds of automated tests & remote parallel execution! Once you have a huge automated regression test suite in place, you might have split it into multiple test suites based on business modules and you might be running them in parallel as part of your daily automated regression. Most of these automated regression suites run in a remote VM and the test results are created / accessible only at the end of the test execution. Otherwise, you might need to look into jenkins console for the test execution results log to see the progress. It might be annoying in certain cases. It might be cool to have an application which gives us the test execution results while the tests are being executed in the remote VMs.

Lets see how we could create a simple test execution results page and a dashboard using ElasticSearch & Kibana.

Elasticsearch:

ElasticSearch is basically a document storage and a Search Engine which exposes REST API for storing and retrieving results based on our query. More info on Elasticsearch is here. To setup Elasticsearch, use docker or check the installation page on the Elasticsearch site for the instructions for your OS.

docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.2.2

Kibana:

Kibana is a front-end application for Elasticsearch. It is basically a data visualization tool for your elasticsearch data. More info on Kibana is here. To setup kibana using docker,

docker run -p 5601:5601 -e ELASTICSEARCH_URL=http://[IP where elasticsearch is running]:9200 docker.elastic.co/kibana/kibana:6.2.2

Maven POM Dependencies:

I love Java & I have included below dependencies in my Maven pom file for the sample code I have included in this article. For other programming languages, continue reading this article to get an idea. This real time results setup can be done for any programming language. It is NOT specific to Java.

<dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.14.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.4</version>
    </dependency>
    <dependency>
        <groupId>com.mashape.unirest</groupId>
        <artifactId>unirest-java</artifactId>
        <version>1.4.9</version>
    </dependency>

Sample Test Class:

Lets create a simple test class as shown here.

public class SampleTest {

    @Test(description = "login")
    public void login(){

    }

    @Test(description = "search for flights", dependsOnMethods = "login")
    public void search(){

    }

    @Test(description = "select flight", dependsOnMethods = "search")
    public void select(){

    }

    @Test(description = "book flight", dependsOnMethods = "select")
    public void book(){

    }

    @Test(description = "logout", dependsOnMethods = "book")
    public void logout(){

    }
}

As soon as a test method is complete, I would like to get the status. Basically, I am looking for below information.

  • Name of test class is being executed
  • Name of the test method / description
  • Status of the test method (PASS / FAIL / SKIPPED)
  • Execution Date and Time.

So, Lets assume that our JSON structure to send the data to Elasticsearch would be more or less as shown here.

{
   "testClass":"com.tag.realtime.SampleTest",
   "description":"login",
   "status":"PASS",
   "executionTime":"2018-03-10T14:18:40.369"
}

You might want to include more info – like Test Environment (DEV, QA, STAGING, PROD), assertion results, exception details etc. Create a sample JSON file to get an idea and the information you want to send to Elasticsearch. Once you are done with your JSON structure, Lets create a simple class as shown below. This class is responsible for getting the information we would like to see in real time. This class instance would be serialized into a JSON using Jackson library.

public class TestStatus {

    @JsonProperty("testClass")
    private String testClass;

    @JsonProperty("description")
    private String description;

    @JsonProperty("status")
    private String status;

    @JsonProperty("executionTime")
    private String executionTime;

    public void setDescription(String description) {
        this.description = description;
    }

    public void setExecutionDate(String executionTime) {
        this.executionTime = executionTime;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void setTestClass(String testClass) {
        this.testClass = testClass;
    }

}

Once we build our JSON, we need to post it into our Elasticsearch instance. Elasticsearch exposes REST APIs. I am creating another simple Java class ResultSender to convert above Java class to a JSON string and insert that into our Elasticsearch instance.

public class ResultSender {

    private static final ObjectMapper OM = new ObjectMapper();
    private static final String CONTENT_TYPE = "Content-Type";
    private static final String CONTENT_TYPE_VALUE = "application/json";
    private static final String ELASTICSEARCH_URL = "http://localhost:9200/app/suite"; 
    
    public static void send(final TestStatus testStatus){
        try {
            Unirest.post(ELASTICSEARCH_URL)
                    .header(CONTENT_TYPE, CONTENT_TYPE_VALUE)
                    .body(OM.writeValueAsString(testStatus)).asJson();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

Now we are ready with building JSON file and posting into our Elasticsearch instance. We still need a listener between our test class and ‘ResultSender’ – which is responsible for monitoring the test execution and calling the ‘ResultSender’ to send the results as soon as a test method is complete. So, Lets create a listener class as shown below.

public class ExecutionListener implements ITestListener {

    private TestStatus testStatus;

    public void onTestStart(ITestResult iTestResult) {
        this.testStatus = new TestStatus();
    }

    public void onTestSuccess(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"PASS");
    }

    public void onTestFailure(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"FAIL");
    }

    public void onTestSkipped(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"SKIPPED");
    }

    public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
        //skip
    }

    public void onStart(ITestContext iTestContext) {
        //skip
    }

    public void onFinish(ITestContext iTestContext) {
        //skip
    }

    private void sendStatus(ITestResult iTestResult, String status){
        this.testStatus.setTestClass(iTestResult.getTestClass().getName());
        this.testStatus.setDescription(iTestResult.getMethod().getDescription());
        this.testStatus.setStatus(status);
        this.testStatus.setExecutionDate(LocalDateTime.now().toString());
        ResultSender.send(this.testStatus);
    }

}

Add the listener on your test classes as shown here.  If you have multiple test classes, you could include the listener in the test suite file instead of adding ‘@Listeners’ for each and every test classes.

@Listeners(ExecutionListener.class)
public class SampleTest {
    // test methods
}

Now everything seems to be ready to send some data to Elasticsearch. Run the test and ensure that you do not get any exception due to the above setup. If everything is fine, access Kibana UI using below URL.

http://localhost:5601

Kibana – Creating Index Pattern:

Before we perform any search, It is necessary to create an index pattern in Kibana. Go to Management tab and create an index pattern as shown here.

kibana-index-creation-01

Click on the Next Step

kibana-index-creation-02

Click on the ‘Create Index Pattern’.

Kibana – Discover Tab:

We can get the test method execution results in the discover tab. Kibana would have already queried Elasticsearch for the results based on the index pattern we had created.

Kibana – Visualize Tab:

To create a dashboard with some test execution results, lets create few visualizations.

Kibana – Dashboard Tab:

Lets finally create the dashboard which gives us the real time results while tests are being executed.

End-To-End Verification:

Lets do one final verification to ensure that everything works and we could get real time results while the tests are being executed. Kibana is set to refresh every 5 seconds. So, there will be a 5 seconds delay in worst case scenario which is OK.

 

Summary:

Setting up real time results using Elasticsearch and Kibana is very easy. It requires only very minimal change in your existing framework. This set up could avoid a lot of team’s frustration if they are currently monitoring the results via console output / logging into the remote machine for results analysis. This article gives only a high level idea. You could further enhance this by including more info like test environment, assertion results, add additional filters to refresh the charts data for specific test environment / tests etc.

Happy Testing & Subscribe 🙂

 

Share This:

21 thoughts on “Selenium WebDriver – Real Time Test Execution Results Using Elasticsearch & Kibana

  1. @vlns. Thanks. I was able to set it up. But now my next query is, when I am sending the data more than once its getting added to the existing stats. I want each execution to be unique. How it can be achieved,

    1. If you want to keep only one record for every test, then modify the elasticsearch url to add one more parameter – http://localhost:9200/app/suite/TCID – here TCID is some unique test id like TC001. Now, keep on executing the same test multiple times. Only the latest record will be maintained. But if you execute the test multiple times as part of daily build, i would let it insert multiple records and use filters to filter by date, build number to get specific search result I want.

  2. Really helpul post. I have done the setup but after execution the execution time is not shown correctly. i mean it shows future time by some 6 hours difference. Please help me.

      1. package com.bosch.resources;

        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.time.LocalDateTime;
        import java.util.Calendar;
        import java.util.Date;
        import java.util.TimeZone;

        import com.bosch.resources.*;
        import org.testng.ITestContext;
        import org.testng.ITestListener;
        import org.testng.ITestResult;

        public class ExecutionListener implements ITestListener {
        private TestStatus testStatus;

        public void onTestStart(ITestResult iTestResult) {
        this.testStatus = new TestStatus();
        }

        public void onTestSuccess(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"PASS");
        }

        public void onTestFailure(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"FAIL");
        }

        public void onTestSkipped(ITestResult iTestResult) {
        this.sendStatus(iTestResult,"SKIPPED");
        }

        public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
        //skip
        }

        public void onFinish(ITestContext iTestContext) {
        //skip
        }

        private void sendStatus(ITestResult iTestResult, String status){
        Date localgmt = localToGMT();
        this.testStatus.setTestClass(iTestResult.getTestClass().getName());
        this.testStatus.setDescription(iTestResult.getMethod().getDescription());
        this.testStatus.setStatus(status);
        this.testStatus.setExecutionDate(gmttoLocalDate(localgmt).toString());
        System.out.println(LocalDateTime.now().toString());
        RealTimeResultSender.send(this.testStatus);
        }

        public void onStart(ITestContext context) {

        }

        public static Date gmttoLocalDate(Date date) {
        String timeZone = Calendar.getInstance().getTimeZone().getID();
        Date local = new Date(date.getTime() + TimeZone.getTimeZone(timeZone).getOffset(date.getTime()));
        return local;
        }
        public static Date localToGMT() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date gmt = new Date(sdf.format(date));
        return gmt;
        }

        }
        this is the code i have used but still the time and date are different in kibana report. Please help me.

  3. Awesome!
    Need some guidance
    Can this be implemented without listeners? Having a requirement to accommodate this in an existing framework which is having customized HTML reporting

  4. Not able to find the “app” index in the create index pattern, am i missing something, since i dint use docker to install Elastic search and Kibana.

    1. Nothing to do with docker. Kibana ui might have changed. Just get the high level idea from the post and try to implement on your own.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.