Extent Report in Kotlin

ExtentReportKotlin

   We know that TestNG itself providing an HTML report and the report will generate in the test-output folder of your project structure. In my previous articles, already discussed the implementation of Extent Report with TestNG+Java and NUnit+C#. Here, I would like to share the details on the implementation of the Extent Report using Kotlin language and controlled by TestNG.

    We know that Kotlin is a general-purpose, open-source, statically typed programming language initially designed for the JVM (Java Virtual Machine) and Android that combines object-oriented and functional programming features. Let’s discuss the implementation of the Extent Report using Kotlin language.

Extent Report using Kotlin

Prerequisites

Following are the prerequisites to implement Extent Report using Kotlin,

  • Install Java SDK 8 and above.
  • Install Eclipse or IntelliJ IDEA IDEs.
  • Install the Kotlin plugin in IDEs. Here I am using Eclipse as IDE and Kotlin plugin for Eclipse downloaded from https://marketplace.eclipse.org/content/kotlin-plugin-eclipse
  • The latest version of following maven dependencies:
    • testng
    • selenium-java
    • selenium-server
    • kotlin-test
    • kotlin-stdlib-jdk8
    • extentreports

Step-by-step procedure

Step 1: Create a maven project and add the above dependencies in pom.xml of the project.

Step 2: Create a Kotlin class to keep the logic to generate the Extent Report. Let say the class name as AutomationReport. Here I am using ITestListener interface of TestNG to control the executions and results. Below is the code snippet of the AutomationReport class to implements the ITestListener and declare the objects for ExtentSparkReporter, ExtentReports, and ExtentTest,

class AutomationReport : ITestListener {

public lateinit var sparkReporter: ExtentSparkReporter
public lateinit var extentReport: ExtentReports
public lateinit var extentTest: ExtentTest

…}

Step 3: Create an override method onStart() with logic to generate an HTML template for the test report,

override fun onStart(testContext: ITestContext) {
try {
sparkReporter = ExtentSparkReporter(System.getProperty(“user.dir”) + “/AutomationReport/”)
sparkReporter.config().setDocumentTitle(“Kotlin Automation”)
sparkReporter.config().setReportName(“Automation Execution Report”)
sparkReporter.config().setTheme(com.aventstack.extentreports.reporter.configuration.Theme.DARK)
extentReport = ExtentReports()
extentReport.attachReporter(sparkReporter)
extentReport.setSystemInfo(“Application Name”, “Kotlin Report Demo”)
extentReport.setSystemInfo(“Platform”, System.getProperty(“os.name”))
extentReport.setSystemInfo(“Environment”, “QA”)

} catch (e: Exception) {
e.printStackTrace()
}
}

Step 4: Create an override method onTestStart() with logic to collect current test case name and add it to the report,

override fun onTestStart(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
extentTest = extentReport.createTest(testName)
}

Step 5: Create an override method onTestSuccess() with logic to add pass status to the report,

override fun onTestSuccess(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.PASS,
MarkupHelper.createLabel(testName + ” Test Case PASSED”, ExtentColor.GREEN)
)
} catch (e: Exception) {
e.printStackTrace()
}
}

Step 6: Create an override method onTestSkipped() with logic to add skip status to the report,

override fun onTestSkipped(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.SKIP,
MarkupHelper.createLabel(testName + ” Test Case SKIPPED”, ExtentColor.ORANGE)
)
} catch (e: Exception) {
e.printStackTrace()
}
}

Step 7: Create an override method onTestFailure() with logic to add fail status to the report,

override fun onTestFailure(result: ITestResult) {
var driver: WebDriver
var currentClass = result.getInstance()
var testName: String = result.getMethod().getMethodName()
try {
driver = (currentClass as AutomationBase).getDriverInstance()
var screenshotPath = Utilities().screenshotCapture(driver, result.getName())
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(testName + ” Test Case FAILED”, ExtentColor.RED)
)
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(“Reason for Failure: ” + result.getThrowable().toString(), ExtentColor.RED)
)
extentTest.addScreenCaptureFromPath(screenshotPath)
} catch (e: Exception) {
e.printStackTrace()
}
}

Step 8: Create an override method onFinish() with logic to store HTML report to the specified path  and flush extent report instance,

override fun onFinish(testContext: ITestContext) {
try {
extentReport.flush()
val dateFormat = SimpleDateFormat(“dd-MMM-yyyy_HH-mm-ss”)
val date = Date()
val filePathdate: String = dateFormat.format(date).toString()
var actualReportPath: String = System.getProperty(“user.dir”) + “/AutomationReport/” + “index.html”
File(actualReportPath).renameTo(
File(
System.getProperty(“user.dir”) + “/AutomationReport/”
+ “Automation_Report_” + filePathdate + “.html”
)
)
} catch (e: Exception) {
e.printStackTrace()
}
}

Step 9: Create another class called Utilities to keep common utility functions required for automation. Here I just added one utility to capture the screenshot. In onTestFailure() method, I already used a method called screenshotCapture(). Below is the code snippet to capture the screenshot,

fun screenshotCapture(driver: WebDriver, fileName: String): String {
var destination: String = “”
try {
var scrFile = (driver as TakesScreenshot).getScreenshotAs(OutputType.FILE)
var dateFormat = SimpleDateFormat(“yyyyMMddHHmmss”)
var cal = Calendar.getInstance()
var path = File(“Failure_Screenshots”).getAbsolutePath()
destination = path + “/” + fileName + dateFormat.format(cal.getTime()) + “.png”
scrFile.copyTo(File(destination))
} catch (e: Exception) {
e.printStackTrace()
}
return destination
}

Step 10: Prior to starting the automation execution, you have to map the AutomationReport class to your test runner class (the class which starting your driver instance within TestNG annotation). You can represent AutomationReport class in test runner class as below,

@Listeners(AutomationReport::class)

Step 11: Now you can run your test cases using testng.xml and once execution complete the HTML report will generate inside the AutomationReport folder in the project structure.

     I hope you got an idea of Extent Report implementation using Kotlin language. Try to use the above step-by-step procedure in your automation world and explore it.

make it perfect!

 

Kickstart Selenium with Kotlin

SelKotlin

    We know that Kotlin is a general-purpose, open-source, statically typed programming language initially designed for the JVM (Java Virtual Machine) and Android that combines object-oriented and functional programming features. It is focused on interoperability, safety, clarity, and tooling support. Kotlin originated at JetBrains and has been open-source since 2012. JetBrains uses Kotlin in many of its products including its flagship IntelliJ IDEA.

    In this article, I would like to explain some advantages of Kotlin and the Selenium script development using Kotlin (also covered the extent report logic written in Kotlin language). 

Advantages of Kotlin

  • Code written in Kotlin works with Java as well as it does natively.
  • Kotlin offers first-class support for functional programming, even though it is mainly object-oriented.
  • Kotlin has a good compiler.
  • Kotlin supports seamless integration with the existing infrastructure. Kotlin’s compatible with all Java frameworks and libraries, and it’s designed to integrate easily with Maven and Gradle build systems.
  • Kotlin provides enhanced run-time performance. 
  • Kotlin is easier and cheaper to maintain. 

Kickstart Your Selenium with Kotlin

Prerequisites

Following are the prerequisites to start Selenium with Kotlin,

  • Install Java SDK 8 and above.
  • Install Eclipse or IntelliJ IDEA IDEs.
  • Install the Kotlin plugin in IDEs. Here I am using Eclipse as IDE and Kotlin plugin for Eclipse downloaded from https://marketplace.eclipse.org/content/kotlin-plugin-eclipse 
  • The latest version of following maven dependencies:
    • testng
    • selenium-java
    • selenium-server
    • kotlin-test
    • kotlin-stdlib-jdk8
    • extentreports 

Step-by-step procedure

Step 1: Create a maven project and add the above dependencies in pom.xml of the project.

Step 2: Create a Kotlin class to keep the logic to initiate the driver. Let say AutomationBase. I am defining this class as an abstract class. So it is easy to extend by other classes. In this class, I have implemented three methods startBrowser, getDriverInstance, and setDriverInstance. Below are the sample code snippets:

public lateinit var driver: WebDriver
fun startBrowser() {
try {
System.setProperty(“webdriver.chrome.driver”, “.//chromedriver.exe”)
driver = ChromeDriver()
driver.manage().window().maximize()
} catch (e: Exception) {
e.printStackTrace()
}
}

fun getDriverInstance(): WebDriver {
return this.driver
}

fun setDriverInstance(driver: WebDriver) {
this.driver = driver
}

     The startBrowser helps to create a driver session, here I used chromedriver to start the session and chromdriver.exe added in the project structure. 

Step 3: Create a TestRunner class and it extends AutomationBase class. This class holds TestNG annotations @Listeners, @BeforeClass, and @AfterSuite. Below are the complete code snippets:

@Listeners(AutomationReport::class)
open class TestRunner : AutomationBase() {

@BeforeClass
fun setUp() {
startBrowser()
}

@AfterSuite
fun tearDown() {
Thread.sleep(3000)
driver.quit()
}
}

Step 4: Create a sample test class to keep the test cases. Let say SampleTests and it extends TestRunner class. Following are the sample code snippets:

class SampleTests : TestRunner() {

@Test
fun TC001_testLoadBrowser() {
var appURL: String = “https://www.google.com/
driver.get(appURL)
}

@Test
fun TC002_testSearch() {
SampleTestsHelper.searchValue(driver, “journeyofquality.wordpress.com”)
}

@Test
fun TC003_testSearchFail() {
Assert.fail()
}
}

Step 5: Create a Kotlin class which should act as a helper class of test class. Let say SampleTestsHelper and defined this SampleTestsHelper as object. So Kotlin will create an instance automatically when we invoke the methods of SampleTestsHelper, no need to create a separate instance to invoke the methods of the helper class. Below is the complete code snippets:

object SampleTestsHelper {
/**
* This method used to search value
*
* @author sanojs
* @since 24-05-2020
*/
fun searchValue(driver: WebDriver, valueToSearch: String) {
try {
driver.findElement(By.name(“q”)).sendKeys(valueToSearch)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 6: Create a Kotlin class to keep the logic to generate an Automation test execution report in HTML format. Let say the class name as AutomationReport. Here I am using extentreports library to generate an HTML report. Also, using ITestListener interface to control the executions and results. Below is the complete code snippet of the AutomationReport class,

class AutomationReport : ITestListener {

public lateinit var sparkReporter: ExtentSparkReporter
public lateinit var extentReport: ExtentReports
public lateinit var extentTest: ExtentTest
/**
* This override method used to create HTML template for the test report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onStart(testContext: ITestContext) {
try {
sparkReporter = ExtentSparkReporter(System.getProperty(“user.dir”) + “/AutomationReport/”)
sparkReporter.config().setDocumentTitle(“Selenium Kotlin Automation”)
sparkReporter.config().setReportName(“Automation Execution Report”)
sparkReporter.config().setTheme(com.aventstack.extentreports.reporter.configuration.Theme.DARK)
extentReport = ExtentReports()
extentReport.attachReporter(sparkReporter)
extentReport.setSystemInfo(“Application Name”, “Kotlin Selenium Demo”)
extentReport.setSystemInfo(“Platform”, System.getProperty(“os.name”))
extentReport.setSystemInfo(“Environment”, “QA”)

} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to collect current test case name add to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestStart(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
extentTest = extentReport.createTest(testName)
}
/**
* This override method used to add pass status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestSuccess(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.PASS,
MarkupHelper.createLabel(testName + ” Test Case PASSED”, ExtentColor.GREEN)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add fail status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestFailure(result: ITestResult) {
var driver: WebDriver
var currentClass = result.getInstance()
var testName: String = result.getMethod().getMethodName()
try {
driver = (currentClass as AutomationBase).getDriverInstance()
var screenshotPath = Utilities().screenshotCapture(driver, result.getName())
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(testName + ” Test Case FAILED”, ExtentColor.RED)
)
extentTest.log(
Status.FAIL,
MarkupHelper.createLabel(“Reason for Failure: ” + result.getThrowable().toString(), ExtentColor.RED)
)
extentTest.addScreenCaptureFromPath(screenshotPath)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to add skip status to the report
*
* @author sanojs
* @since 25-05-2020
*/
override fun onTestSkipped(result: ITestResult) {
var testName: String = result.getMethod().getMethodName()
try {
extentTest.log(
Status.SKIP,
MarkupHelper.createLabel(testName + ” Test Case SKIPPED”, ExtentColor.ORANGE)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* This override method used to store HTML report to the specified path and flush extent report instance
*
* @author sanojs
* @since 25-05-2020
*/
override fun onFinish(testContext: ITestContext) {
try {
extentReport.flush()
val dateFormat = SimpleDateFormat(“dd-MMM-yyyy_HH-mm-ss”)
val date = Date()
val filePathdate: String = dateFormat.format(date).toString()
var actualReportPath: String = System.getProperty(“user.dir”) + “/AutomationReport/” + “index.html”
File(actualReportPath).renameTo(
File(
System.getProperty(“user.dir”) + “/AutomationReport/”
+ “Automation_Report_” + filePathdate + “.html”
)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

Step 7: Create another class called Utilities to keep common utility functions required for automation. Here I just added one utility to capture the screenshot. Below is the code snippet to capture the screenshot,

fun screenshotCapture(driver: WebDriver, fileName: String): String {
var destination: String = “”
try {
var scrFile = (driver as TakesScreenshot).getScreenshotAs(OutputType.FILE)
var dateFormat = SimpleDateFormat(“yyyyMMddHHmmss”)
var cal = Calendar.getInstance()
var path = File(“Failure_Screenshots”).getAbsolutePath()
destination = path + “/” + fileName + dateFormat.format(cal.getTime()) + “.png”
scrFile.copyTo(File(destination))
} catch (e: Exception) {
e.printStackTrace()
}
return destination
}

Step 8: Add a testng.xml file into the project structure and map your test classes into the testng.xml to run the test cases. Once execution complete the HTML report will generate inside the AutomationReport folder in the project structure. 

Below is the overall project structure of Selenium with Kotlin,

Selenium with Kotlin Structure

NOTE: 

  • Kotlin Runtime Library will automatically add to the build path of the project when you create a Kotlin class under any package. 
  • Kotlin class extension is .kt
  • The semicolon is not mandatory to end the statements in the class.
  • While inheriting a class from a parent class you have to declare the parent class as open using an open keyword or you can declare the parent class as abstract using abstract. You can’t inherit a singleton class. So avoid to extend the class which declared as object in Kotlin.
  • If you declare a class with an object keyword then no need to create an instance, Kotlin will create an instance to access the methods or variables of that class. 

Try to use the above step-by-step procedure in your automation world and enjoy it. 

make it perfect!