TestKase Docs
AutomationTest Frameworks

Selenium

Set up Selenium WebDriver for browser testing and integrate test results with TestKase.

Overview

Selenium is an open-source browser automation framework that supports multiple programming languages including Java, Python, JavaScript, and C#. It uses the WebDriver protocol to control browsers programmatically, making it the most widely adopted tool for end-to-end web testing.

Selenium itself does not produce test reports — it relies on the test runner you pair it with (JUnit, TestNG, pytest, etc.). To integrate with TestKase, use:

  • --format junit when running Selenium tests with JUnit (Java) or pytest (Python)
  • --format testng when running Selenium tests with TestNG (Java)

Prerequisites

  • Java: Java 11 or later, Maven 3.6+
  • Python: Python 3.8 or later, pip
  • Node.js: Node.js 18 or later (for JavaScript bindings)
  • Browser drivers: ChromeDriver, GeckoDriver (Firefox), or EdgeDriver matching your installed browser version
  • A TestKase account with a project, test cycle, and Automation IDs configured on your test cases

Installation

Java (Maven)

Add the following dependencies to your pom.xml:

<dependencies>
    <!-- Selenium WebDriver -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.18.1</version>
    </dependency>

    <!-- JUnit 5 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>

    <!-- WebDriverManager (auto-manages browser drivers) -->
    <dependency>
        <groupId>io.github.bonigarcia</groupId>
        <artifactId>webdrivermanager</artifactId>
        <version>5.7.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Python

pip install selenium pytest

Project Setup

Java — Maven Project Structure

my-selenium-project/
├── pom.xml
└── src/
    └── test/
        └── java/
            └── com/
                └── example/
                    ├── LoginTest.java
                    └── SearchTest.java

Your pom.xml should include the Maven Surefire Plugin (included by default in most Maven projects) which generates JUnit XML reports in target/surefire-reports/:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.2.5</version>
        </plugin>
    </plugins>
</build>

Python — Project Structure

my-selenium-project/
├── requirements.txt
├── conftest.py
└── tests/
    ├── test_login.py
    └── test_search.py

requirements.txt:

selenium>=4.18.0
pytest>=8.0.0

Writing Tests

Java — JUnit 5 Test Class

package com.example;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.*;

public class LoginTest {

    private WebDriver driver;
    private WebDriverWait wait;

    @BeforeEach
    void setUp() {
        WebDriverManager.chromedriver().setup();
        driver = new ChromeDriver();
        wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        driver.get("https://example.com/login");
    }

    @AfterEach
    void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    @DisplayName("[48271] testValidLogin")
    void testValidLogin() {
        driver.findElement(By.id("username")).sendKeys("testuser");
        driver.findElement(By.id("password")).sendKeys("password123");
        driver.findElement(By.id("login-btn")).click();

        WebElement dashboard = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.id("dashboard"))
        );
        assertTrue(dashboard.isDisplayed());
    }

    @Test
    @DisplayName("[48272] testInvalidLogin")
    void testInvalidLogin() {
        driver.findElement(By.id("username")).sendKeys("invalid");
        driver.findElement(By.id("password")).sendKeys("wrong");
        driver.findElement(By.id("login-btn")).click();

        WebElement error = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.className("error-message"))
        );
        assertEquals("Invalid credentials", error.getText());
    }
}

Each test includes a 5-digit Automation ID in its @DisplayName annotation using square brackets. The @testkase/reporter CLI extracts these IDs using the regex \[(\d{5})\]. For the tests above:

  • 48271 → linked to the "valid login" test case in TestKase
  • 48272 → linked to the "invalid login" test case in TestKase

Python — pytest Test File

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class TestLogin:
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://example.com/login")
        self.wait = WebDriverWait(self.driver, 10)

    def teardown_method(self):
        if self.driver:
            self.driver.quit()

    def test_valid_login(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-btn").click()

        dashboard = self.wait.until(
            EC.presence_of_element_located((By.ID, "dashboard"))
        )
        assert dashboard.is_displayed()

    def test_invalid_login(self):
        self.driver.find_element(By.ID, "username").send_keys("invalid")
        self.driver.find_element(By.ID, "password").send_keys("wrong")
        self.driver.find_element(By.ID, "login-btn").click()

        error = self.wait.until(
            EC.presence_of_element_located((By.CLASS_NAME, "error-message"))
        )
        assert error.text == "Invalid credentials"

Embed 5-digit Automation IDs in your test names using square brackets so they appear in the JUnit XML output. For pytest, use @pytest.mark or include the ID in the test's parametrize label:

  • [48271] → linked to the "valid login" test case in TestKase
  • [48272] → linked to the "invalid login" test case in TestKase

Running Tests

Java (Maven)

mvn test

JUnit XML reports are generated automatically in target/surefire-reports/. Each test class produces a file named TEST-<className>.xml (e.g., TEST-com.example.LoginTest.xml).

Python (pytest)

pytest --junitxml=test-results/junit.xml

This produces a single JUnit XML file at test-results/junit.xml.

TestKase Integration

After running your tests and generating the results file, use the @testkase/reporter CLI to push results to TestKase.

Java (JUnit XML from Maven Surefire)

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file "target/surefire-reports/TEST-*.xml"

--cycle-id is optional. If not provided, results are reported to TCYCLE-1 — the master test cycle for the project.

Python (JUnit XML from pytest)

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file test-results/junit.xml

Use --dry-run --verbose the first time to verify that your test names match the Automation IDs you set in TestKase. This helps catch naming mismatches before pushing real results.

Automation ID Mapping

The reporter extracts 5-digit Automation IDs from test names using the [XXXXX] bracket pattern:

RunnerWhere to Embed the IDExample
Java (JUnit 5)@DisplayName annotation@DisplayName("[48271] testValidLogin")
Java (TestNG)@Test(description = "...")@Test(description = "[48271] testValidLogin")
Python (pytest)Test docstring or parametrize ID"""[48271] Valid login test"""
JavaScript (Mocha/Jest)it() or test() nametest('[48271] should authenticate user', ...)

Generate Automation IDs in TestKase first, then embed them in your test names. Use --dry-run to verify which [XXXXX] IDs the reporter extracts from your results file.

Complete Example

This end-to-end example shows the full flow for a Java Selenium project with JUnit 5.

1. Create the Test

package com.example;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.*;

public class SearchTest {

    private WebDriver driver;
    private WebDriverWait wait;

    @BeforeEach
    void setUp() {
        WebDriverManager.chromedriver().setup();
        driver = new ChromeDriver();
        wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    }

    @AfterEach
    void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    @DisplayName("[48271] testSearchReturnsResults")
    void testSearchReturnsResults() {
        driver.get("https://example.com");
        driver.findElement(By.id("search-input")).sendKeys("selenium");
        driver.findElement(By.id("search-btn")).click();

        WebElement results = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.id("search-results"))
        );
        assertTrue(results.isDisplayed());
    }
}

2. Set the Automation ID in TestKase

In your TestKase project, generate an Automation ID (e.g., 48271) on the corresponding test case, then embed it in your test's @DisplayName annotation: @DisplayName("[48271] testSearchReturnsResults")

3. Run the Tests

mvn test

4. Verify with Dry Run

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file "target/surefire-reports/TEST-com.example.SearchTest.xml" \
  --dry-run --verbose

5. Push Results

Remove the --dry-run flag to push results to TestKase:

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file "target/surefire-reports/TEST-com.example.SearchTest.xml"

Troubleshooting

WebDriver not found

org.openqa.selenium.SessionNotCreatedException: Could not start a new session.

Cause: The browser driver (ChromeDriver, GeckoDriver) is not installed or not on your PATH.

Fix: Use WebDriverManager to automatically download and configure the correct driver version:

// Add to your test setup:
WebDriverManager.chromedriver().setup();

Or install drivers manually and add them to your system PATH.

Stale element references

org.openqa.selenium.StaleElementReferenceException: stale element reference

Cause: The DOM element was modified or removed after you located it.

Fix: Use WebDriverWait with ExpectedConditions to wait for elements to be present or visible before interacting with them:

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(
    ExpectedConditions.presenceOfElementLocated(By.id("my-element"))
);

JUnit XML not generated

Cause: The Maven Surefire Plugin is not configured or the test phase was skipped.

Fix: Ensure the Surefire Plugin is included in your pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.2.5</version>
</plugin>

Verify that target/surefire-reports/ contains XML files after running mvn test. If the directory is empty, check that your test classes follow the Surefire naming convention (*Test.java, Test*.java, or *Tests.java).