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!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s