Quickstart
This guide shows how to write and run your first end-to-end test using Parikshan.
1. Create an E2E Test
Create a test class inside your shared library's commonTest directory (e.g., composeApp/src/commonTest/kotlin/sample/app/LoginTest.kt).
Wrap your test body inside the e2eTest block:
package sample.app
import io.github.aryapreetam.parikshan.e2eTest
import kotlin.test.Test
class LoginTest {
@Test
fun testSuccessfulLogin() = e2eTest {
// Input text into fields matching Modifier.testTag
input("username_field", "admin")
input("password_field", "password123")
// Click on the login button
click("login_button")
// Assertions
assertVisible("dashboard_screen")
assertText("welcome_header", "Welcome back, admin!")
// Capture screenshot
screenshot("dashboard-success")
}
}
2. Execute Tests
Specific Target Execution
Run dedicated tasks for individual targets:
# Run on Desktop JVM
./gradlew :composeApp:e2eDesktopTest
# Run on Web (WasmJs) via Playwright
./gradlew :composeApp:e2eWasmTest
# Run on Android Emulator or connected device
./gradlew :composeApp:e2eAndroidTest
# Run on iOS Simulator
./gradlew :composeApp:e2eIosTest
Orchestrated Suite Execution
Run tests concurrently across targets using e2eTest:
Filter by specific target platforms or test class names:
# Target specific platforms only
./gradlew :composeApp:e2eTest --targets=desktop,wasm
# Filter by test class
./gradlew :composeApp:e2eTest --tests "sample.app.LoginTest"
3. View Execution Reports
When you run the unified e2eTest task, individual target test results are aggregated into an HTML report under:
Open this file in a browser to inspect pass/fail statuses, failure stack traces, and failure screenshots across targets.
4. Test Lifecycle and State Resets
Parikshan distinguishes between Synchronous Infrastructure Hooks and UI State Resets.
Synchronous Infrastructure Hooks (@BeforeAll, @AfterAll, @BeforeTest, @AfterTest)
Standard Kotlin/JUnit test annotations (@BeforeAll, @AfterAll, @BeforeTest, @AfterTest) execute synchronous Kotlin code outside of e2eTest. Use these for non-UI setup:
* Database seeding and cleanup
* Mock server initialization (e.g. configuring Ktor MockEngine or local HTTP endpoints)
* Resetting mock registries (e.g. ServiceRegistry.resetForTesting())
import io.github.aryapreetam.parikshan.BeforeAll
import io.github.aryapreetam.parikshan.AfterAll
import kotlin.test.BeforeTest
import kotlin.test.AfterTest
import kotlin.test.Test
import io.github.aryapreetam.parikshan.e2eTest
class UserRegistrationTest {
companion object {
@BeforeAll
fun seedDatabaseAndStartMockServer() {
MockDatabase.seedTestUsers()
MockKtorServer.start(port = 8080)
}
@AfterAll
fun teardownServer() {
MockKtorServer.stop()
MockDatabase.clear()
}
}
@BeforeTest
fun setupMockEngine() {
MockKtorServer.resetEndpoints()
}
@AfterTest
fun cleanupRegistry() {
ServiceRegistry.resetForTesting()
}
@Test
fun testRegistration() = e2eTest {
input("email_field", "newuser@example.com")
click("register_button")
assertVisible("confirmation_screen")
}
}
UI Lifecycle Hooks (E2ETestLifecycle)
To perform UI resets before or after each test using Parikshan DSL commands, implement the E2ETestLifecycle interface on your test class.
Override beforeEach() and afterEach() to execute UI navigation commands inside the active E2ETestScope receiver context:
import io.github.aryapreetam.parikshan.E2ETestLifecycle
import io.github.aryapreetam.parikshan.E2ETestScope
import io.github.aryapreetam.parikshan.e2eTest
import kotlin.test.Test
class SettingsFlowTest : E2ETestLifecycle {
override suspend fun E2ETestScope.beforeEach() {
// Executes inside E2ETestScope before each test
click("nav_settings_tab")
assertVisible("settings_screen")
}
override suspend fun E2ETestScope.afterEach() {
// Executes inside E2ETestScope after each test
click("nav_home_tab")
assertVisible("home_screen")
}
@Test
fun testToggleDarkMode() = e2eTest {
click("dark_mode_switch")
assertVisible("dark_theme_active")
}
}
Use relaunchApp() sparingly on Desktop JVM
relaunchApp() destroys and re-launches the application process or Desktop window, introducing a 1 to 2-second boot latency per test execution on Desktop JVM targets (on WasmJs it is a fast browser reload). Avoid running it sequentially across many tests; use in-app UI navigation resets (E2ETestLifecycle) for fast test teardown under 100ms.