Why E2E Testing Is Your Best Friend (and How to Befriend It in Kotlin)
\n\nImagine: you wrote a cool service in Kotlin, everything compiled, unit tests are green, code review passed. You deploy to production, and... users complain that the "Buy" button doesn't work, even though unit tests checked it. Sound familiar? Welcome to the world where E2E testing Kotlin applications is not a luxury, but a necessity.
\n\nEnd-to-end testing is checking the entire user path: from a button click to a database write and back. In this article, we won't just talk about theory. We'll write real tests for a Kotlin application, break down the tools, and show you how not to go crazy setting up infrastructure. Buckle up, there will be code and coffee.
\n\nWhat Is E2E Testing and How It Differs from Other Types
\n\nLet's set the record straight. E2E (or end-to-end) testing checks the entire system as the user sees it. Unlike:
\n\n- \n
- Unit tests — check one method/class in isolation. \n
- Integration tests — check the interaction of two or three components (e.g., controller + service). \n
- UI tests — often confused with E2E, but a UI test may not check the backend, only the interface rendering. \n
E2E testing Kotlin applications is a full cycle: user interface (if any), API, business logic, database, external services. Everything must work together like clockwork.
\n\nTools for E2E Testing in Kotlin
\n\nThe choice of tool depends on what you are testing: an Android application, a Ktor web service, or a desktop application. Here are the top picks for a Kotlin developer:
\n\n- \n
- Selenide + Kotlin — for web interfaces. A wrapper over Selenium, but with concise syntax, perfect for Kotlin. \n
- Ktor Client + TestContainers — for the backend. Spin up test infrastructure (DB, cache) in Docker and fire HTTP requests. \n
- Espresso + Compose Test — for Android. Native tools that come with the Android SDK. \n
- Cucumber + Kotlin — if you need BDD scenarios in a language understandable to the client. \n
We'll focus on the first two scenarios, as they are the most universal.
\n\nWriting an E2E Test for a Kotlin Web Application (Selenide)
\n\nSuppose we have a simple online store on Spring Boot + Kotlin. We need to check the scenario: a user visits the site, adds a product to the cart, and places an order.
\n\nFirst, add the dependencies to build.gradle.kts:
dependencies {\n testImplementation("com.codeborne:selenide:6.19.1")\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n testImplementation("io.github.bonigarcia:webdrivermanager:5.6.2")\n}\n\nNow let's write the test itself. Note: we use Kotlin extension functions to make the code readable:
\n\nimport com.codeborne.selenide.Condition.visible\nimport com.codeborne.selenide.Selenide.`$`\nimport com.codeborne.selenide.Selenide.open\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass ShopE2ETest {\n\n @BeforeEach\n fun setUp() {\n // Open the browser before each test\n open("https://test-shop.example.com")\n }\n\n @Test\n fun `user can add product to cart and checkout`() {\n // Step 1: Find the product and add to cart\n `$`("[data-testid='product-1']").shouldBe(visible).click()\n `$`("[data-testid='add-to-cart']").click()\n\n // Step 2: Verify the cart is not empty\n `$`("[data-testid='cart-badge']").shouldHave(text("1"))\n\n // Step 3: Proceed to checkout\n `$`("[data-testid='checkout-btn']").click()\n\n // Step 4: Fill out the form\n `$`("[