E2E testing with Selenium and Kotlin
Tags: testing, e2e, selenium, kotlin
Published: 2018-01-02 03:39:27

---
title: E2E testing with Selenium and Kotlin
published: true
tags: #testing #e2e #selenium #kotlin
cover_image: https://crossbrowsertesting.com/blog/wp-content/uploads/2017/07/080117_Selenium-Challenges.png
---

# BE AWARE

#### This will be a looong post. So, grab a cup of coffee/tea and hang on tight!

# Introduction

### What is E2E?

_End-To-End (E2E) testing_ is a technique used to test an entire flow as if we were an actual user by simulating their actions (clicks, pressing certain keys, typing into a field, etc) on the browser.

### What is Selenium?

_Selenium_ is an automation framework for web applications.

### What is Kotlin?

_Kotlin_ is a statically typed programming language made by [Jetbrains](https://www.jetbrains.com/) that can be used for web, mobile and desktop applications on both the frontend and backend. We will be using it on the JVM.

### What is love?

_Baby don't hurt me..._

Sorry, I had to do it!

# Requirements

## IDE

I'll be using the IntelliJ IDEA IDE, also made by Jetbrains.

From their [download page](https://www.jetbrains.com/idea/download/), select the Community Edition (free).

![IntelliJ IDEA download page](https://thepracticaldev.s3.amazonaws.com/i/gxu9ss9m2ahblelf2jye.png)

## Selenium

Go to Selenium's [download page](http://www.seleniumhq.org/download/) and download the .jar file under the "Selenium Standalone Server" section. 

![Selenium download page](https://thepracticaldev.s3.amazonaws.com/i/xlr45xu3aqnp9jn9fowl.png)

![Selenium download link](https://thepracticaldev.s3.amazonaws.com/i/6z7bfbnwrub40t5kvxjy.png)

## NodeJS

This can be done via their [website](https://nodejs.org/en/download/) or via your operating system's terminal/command line (steps will be different depending on your OS).

After that, in order to verify that your NodeJS was installed successfully, run the following command in your terminal/command line:

`npm -v`

You should be able to see the version number, mine is 5.6.0 as I write this post.

## Browser Drivers

With the `npm` installed on our machine, we can now install the browser driver that we need. For this case we will be using Google Chrome (here's the [documentation](https://github.com/SeleniumHQ/selenium/wiki) if you want to use a different one by searching for "driver" on the right hand navigation bar).

### Chromedriver

In order to install the `chromedriver` you will have to run the following command on your terminal/command line:

`npm install chromedriver`

Now double check that the installation was done successfully by running:

`chromedriver -v`

### Awesome, but...

# Where's the code, tho?

![Where?!](https://thepracticaldev.s3.amazonaws.com/i/w6d1imumvw5xjw3jiesn.gif)

Go ahead and open up IntelliJ IDEA and follow the basic setup steps. 

Don't worry, take your time, I can wait.

Meet me at the "Welcome to IntelliJ IDEA" screen.

![Welcome to IntelliJ IDEA](https://thepracticaldev.s3.amazonaws.com/i/1as5xicwwdg4nu4qja1s.png)

Select `Create New Project`, and choose `Kotlin > Kotlin/JVM`.

![Create new project](https://thepracticaldev.s3.amazonaws.com/i/jxxhpg699gv3ealggrgt.png)

Choose a project name, I chose `selenium-kotlin`.

![Project name](https://thepracticaldev.s3.amazonaws.com/i/8gmsfcypt5sww73dozdb.png)

Then, hit the `Finish` button.

Head over to the left pane (project explorer)

![Project explorer](https://thepracticaldev.s3.amazonaws.com/i/qm5jgd3pha7z9u7h7xl2.png)

## Project structure

We will create a few packages to match this structure:

```
selenium-kotlin    
│
└─── .idea
|    |
|    └─── ...
|
└─── src
|    │
|    └─── main
|    |    |
|    |    └─── page
|    |
|    └─── tests
|
└─── selenium-kotlin.iml
```

Now, we need to mark the `main` folder as a `Sources root` and the `tests` folder as a `Test Sources root`. You can achieve this by right clicking the package and selecting `Mark Directory as > ...`

## Add Selenium to the project

Go to the `File` menu and select the `Project Structure...`.

![File menu](https://thepracticaldev.s3.amazonaws.com/i/79u29dsa0ldn3sasll9v.png)

Select the `Modules`.

![Project settings](https://thepracticaldev.s3.amazonaws.com/i/2du3sji37shc5nvtxmc5.png)

Add the selenium jar to the project.

![Add dependency](https://thepracticaldev.s3.amazonaws.com/i/1g1y373qkmm12c8i1upd.png)

Look for the .jar file we downloaded earlier. It might be in your `./Downloads` folder, unless you saved it somewhere else.

Click the `OK` button on the bottom right of the `Project Structure` window and you should now be able to see selenium under the `External Libraries` in your `Project Explorer`.

## Create your first Page Object Model

We can _finally_ start coding!

> _Page Object Model_ is a design pattern that helps us organize where all the `WebElement` are based on to which "Page" they belong to. This helps us reduce code duplication across different parts of our codebase.

Head over to your `src/main/kotlin/page` folder. In this package, we will create classes that represent a given page in our web applications.

![GitHub Home Page](https://thepracticaldev.s3.amazonaws.com/i/pdae8wuz85mm64rif5jl.png)

Like the one above that would be represented by `GitHubHomePage.kt`. So, go ahead and make it. Right click on the `page` folder, select `New > Kotlin File/Class`.

Your file will be (almost) empty:

```kotlin
package page

```

Go ahead and add the class definition:

```kotlin
class GitHubHomePage {

}
```

The first thing we need to do with our page is to add an `open()` method. Since we will want to visit it with a direct URL during our test.

```kotlin
class GitHubHomePage {

    fun open() {
        // Go to GitHub's home page
    }

}
```

In order to do this, we will need a `WebDriver` object, this is a class provided by Selenium that represents our browsers (Chrome, Firefox, Safari, Internet Explorer, etc) and all it's methods to control the browser.

We will need to pass it in inside our `GitHubHomePage`'s constructor, and then use it inside our `open()` method by calling the `WebDriver`'s method called `get()`.

```kotlin
class GitHubHomePage(private val driver: WebDriver) {

    fun open() {
        driver.get("https://github.com/")
    }

}
```

As you can see, the `get()` method takes in a `String` value that will be GitHub's main page.

> Note: the `get()` method actually calls `navigate().to()`, but `get()` is shorter :)

Now we can run our first test really quick to open a browser and then visit GitHub's website.

## Create your first test

Place your `cursor` on top of the `GitHubHomePage` class name, use the `Find Actions` menu by pressing `SHIFT + CTRL + A` (for Windows) or `SHIFT + CMD + A` (for MacOS).

A search bar dialog will show up and type in `Create test`, then hit `ENTER`.

A new dialog will show up. Select `TestNG` as your `Testing Library` from the dropdown, there will be a warning message saying "TestNG library not found in the module". Don't worry, just press the `Fix` button right next to it. 

Another window will show up and make sure you leave it with the "Use 'testng' from IntelliJ IDEA distribution" radio button active, hit `OK`, and then mark the 2 checkboxes: `setUp/@Before` and `tearDown/@After`.

It should look like this:

![Create test dialog](https://thepracticaldev.s3.amazonaws.com/i/vpkedxpqcrfzcuz1bosm.png)

Then, hit `OK` again.

This will create the test class for you inside the `src/tests/kotlin/page` directory with the `setUp()` and `tearDown()` methods.

The `setUp()` method will always run _before_ each test, while the `tearDown()` will always run _after_ each test.

We will use them to _setup_ our browser, we will use Chrome this time, and we will make sure to always close all the browsers when the tests are done.

Back to the code!

## Open the browser

Inside the `setUp()` method, let's create an instance of the `ChomeDriver` class.

```kotlin
class GitHubHomePageTest {
    private lateinit var driver: WebDriver

    @BeforeMethod
    fun setUp() {
        driver = ChromeDriver()
    }

    @AfterMethod
    fun tearDown() {
        // Close all browsers
    }
}
```

## Go to GitHub's home page

Now let's create an instance of the `GitHubHomePage` class and call it's `open()` method.

```kotlin
class GitHubHomePageTest {
    private lateinit var driver: WebDriver
    private lateinit var gitHubHomePage: GitHubHomePage

    @BeforeMethod
    fun setUp() {
        driver = ChromeDriver()
        gitHubHomePage = GitHubHomePage(driver)
        gitHubHomePage.open()
    }

    @AfterMethod
    fun tearDown() {
        // Close all browsers
    }
}
```

## Close the browser when your are done

Head over to the `tearDown()` method, and call de `quit()` method from the `driver` instance.

```kotlin
class GitHubHomePageTest {
    private lateinit var driver: WebDriver
    private lateinit var gitHubHomePage: GitHubHomePage

    @BeforeMethod
    fun setUp() {
        driver = ChromeDriver()
        gitHubHomePage = GitHubHomePage(driver)
        gitHubHomePage.open()
    }

    @AfterMethod
    fun tearDown() {
        driver.quit()
    }
}
```

Almost there!

Let's add a Test Function, remember how to use the `Generate` menu?

`SHIFT + CTRL + A`/`SHIFT + CMD + A`.

Type in `Generate` and hit `ENTER`.

Select the `Test Function`.

Give it a name like `emptyTest` and put a placeholder comment for now. We just want to see if we can open the browser and go to the home page.

The easiest way to run our `emptyTest` is by clicking the green play button on the left side of the editor pane. Right next to the line #21.

![Run test method](https://thepracticaldev.s3.amazonaws.com/i/tz7uejorvae0wjuxpkmr.png)

![EmptyTest passed](https://thepracticaldev.s3.amazonaws.com/i/ankn0lqv1or4u1osx8gm.png)

![yeah boi](https://media.giphy.com/media/xTiQyAVsNw6mOab8hW/giphy.gif)

# Let's make a real test

How about testing if we can look for a GitHub username and access their profile?

Sounds fairly easy, right?

## Refactor `emptyTest`

The first thing we'll need to do is to rename our `emptyTest` method.

How about... `searchForUsername`? Good.

Now, where is the search bar located? It's right there in the home page.

So let's add a method that will take a `String`, type it into the `searchBox` and submit it. It should look like:

```kotlin
fun searchFor(query: String) {
    val searchBox = driver.findElement(By.xpath("//input[@placeholder='Search GitHub']"))
    searchBox.sendKeys(query, Keys.ENTER)
}
```

Wait, wait, wait...

#### Where did all this `findElement()`, `By`, `xpath`, `sendKeys()` and `Keys` come from!?

Ok, let me explain:

`findElement()` is a method from `driver` that takes a `By.<something>` as an argument. If successful, it returns a `WebElement`; otherwise, it will `throw` a `NoSuchElementException`.

`By.xpath()` takes in a `String` argument that will help us locate an element on the page, in this case we will get whatever `<input>` that has a `placeholder` attribute that matches "Search GitHub" exactly. Other examples are `By.id`, `By.cssSelector`, `By.className`, etc.

`sendKeys()` is a method from the `WebElement` class provided by Selenium that takes an argument of type `CharSequence`, which means we can pass in a `char`, `String` or any values from the `Keys` enum class, like the `Keys.ENTER` which will NOT type in the word "ENTER", but rather simulate that we pressed the `ENTER` key on our keyboard, and thus submitting the query in our example.

### Cool, right? But we are not done yet

Now we gotta filter our results from the search to only see usernames and then click the username we are looking for.

Since this page has elements that are only part of the "searching" flow we will make another Page class and call it `GitHubSearchPage` (Remember to do it in the `src/main/kotlin/page` package).

```kotlin
class GitHubSearchPage(private val driver: WebDriver) {
    
}
```

Add the `WebElement` for the users filter and the `filterByUsers()` method that will `click()` it.

```kotlin
fun filterByUsers() {
    val usersFilter = driver.findElement(By.xpath("//a[text() = 'Users']"))
    usersFilter.click()
}
```

Add another `WebElement` for the user profile link and an `enterUserProfile()` method that will `click()` it.

```kotlin
fun enterUserProfile() {
    val userProfileLink = driver.findElement(By.xpath("//span[text() = 'Christian Vasquez']/../a/em"))
    userProfileLink.click()
}
```

I'll also add a `open()` method so we can go straight to the search page in case we need later.

The entire class should look like:

```kotlin
class GitHubSearchPage(private val driver: WebDriver) {

    fun open() {
        driver.get("https://github.com/search?")
    }

    fun filterByUsers() {
        val usersFilter = driver.findElement(By.xpath("//a[text() = 'Users']"))
        usersFilter.click()
    }

    fun enterUserProfile() {
        val userProfileLink = driver.findElement(By.xpath("//span[text() = 'Christian Vasquez']/../a/em"))
        userProfileLink.click()
    }

}
```

Let's start using our new `GitHubSearchPage` in our `searchForUsername()` test function.

```kotlin
@Test
fun searchForUsername() {
    gitHubHomePage.searchFor("chrisvasqm")
    gitHUbSearchPage = GitHubSearchPage(driver)
    gitHUbSearchPage.filterByUsers()
    gitHUbSearchPage.enterUserProfile()
}
```

Now let's check that we got redirected to the "www.github.com/chrisvasqm" URL correctly by adding the following line to our `searchForUsername()` method:

```kotlin
assertTrue(driver.currentUrl == "https://github.com/chrisvasqm")
```

The entire test function should look like this now:

```kotlin
@Test
fun searForUsername() {
    gitHubHomePage.searchFor("chrisvasqm")
    gitHUbSearchPage = GitHubSearchPage(driver)
    gitHUbSearchPage.filterByUsers()
    gitHUbSearchPage.enterUserProfile()

    assertTrue(driver.currentUrl == "https://github.com/chrisvasqm")
}
```

Let's try to run our test now...

![BOOM!](https://media.giphy.com/media/dpnfPvaCIHBrW/giphy.gif)

We got a `NoSuchElementException`, but worry you must not.

What happened was that our `userProfileLink` WebElement would not be present right away after we click the `usersFilter` element.

In order to fix this we can use 2 classes provided by Selenium: `PageFactory` and `AjaxElementLocatorFactory`. 

The way we do it is by using the `init {...}` block that Kotlin has (it's similar to a constructor, but used only for initialization).

```kotlin
init {
    PageFactory.initElements(AjaxElementLocatorFactory(driver, 15), this)
}
```

This line of code makes sure to initialize our properties so that it waits a maximum of 15 seconds before it throws a `NoSuchElementException`.

## Which properties?

Good question!

Remember we were doing this?

```kotlin
val element = driver.findElement(By.xpath("VALUE"))
```

We will replace it with:

```kotlin
@FindBy(xpath = "VALUE")
private lateinit var element: WebElement
```

Whenever we use the `PageFactory` + `AjaxElementLocatorFactory` combo along with the `@FindBy` annotation, we will get the benefit of the timeout of 15 seconds I mentioned before.

Let's refactor our `GitHubSearchPage` class then

```kotlin
class GitHubSearchPage(private val driver: WebDriver) {

    @FindBy(xpath = "\"//a[text() = 'Users']\"")
    private lateinit var usersFilter: WebElement

    @FindBy(xpath = "//span[text() = 'Christian Vasquez']/../a/em")
    private lateinit var userProfileLink: WebElement

    init {
        PageFactory.initElements(AjaxElementLocatorFactory(driver, 15), this)
    }

    fun open() {
        driver.get("https://github.com/search?")
    }

    fun filterByUsers() {
        usersFilter.click()
    }

    fun enterUserProfile() {
        userProfileLink.click()
    }

}
```

Rerun the test 🤞

![All tests passed](https://thepracticaldev.s3.amazonaws.com/i/ehkvr2gm37vem5llrum8.png)

We finally made it!

![doge](https://thepracticaldev.s3.amazonaws.com/i/fnt1ijqcv1wm9t2wno5z.jpg)

I hope this post taught you something new, if you didn't know about Selenium already.

I know I didn't go deep into why I did some things that are part of Kotlin itself, but that might be a topic for another post in the future ;)

Dunno, maybe this will push you into trying it out by yourself for other things!

# Learn more

Here is another link that you may find useful to [learn more](https://github.com/SeleniumHQ/selenium/wiki/Getting-Started) about _Selenium_.

And in case you may want to learn about an alternative, here's a guide on how to do [E2E testing with TestCafe](https://dev.to/chrisvasqm/e2e-testing-with-testcafe-17jl) using JavaScript and the VS Code editor.