Kaspresso: Android UI Testing with Kakao DSL and Allure Reports
Kaspresso is an Android UI test framework built on top of Espresso and UIAutomator. It solves the two biggest problems with raw Espresso: flaky tests and unreadable test code. With Kaspresso, you get a clean Kotlin DSL (via Kakao), automatic waiting and retry logic, and first-class Allure report integration.
If your Espresso tests are brittle or your test output is hard to parse, Kaspresso fixes both.
Why Kaspresso Over Raw Espresso
Espresso is fast and accurate, but it has rough edges:
- View synchronization works well for simple cases but breaks with custom async loading patterns
- Test failures produce cryptic stack traces with no screenshots
- Test code becomes verbose for even simple interactions
- UIAutomator integration requires manual boilerplate
Kaspresso wraps all of this with a more stable runtime and a readable DSL.
Setup
Add to build.gradle.kts (app module):
dependencies {
androidTestImplementation("com.kaspersky.android-components:kaspresso:1.5.3")
androidTestImplementation("com.kaspersky.android-components:kaspresso-allure-support:1.5.3")
// Kakao DSL (bundled, but explicit import helps IDE)
androidTestImplementation("io.github.kakaocup:kakao:3.0.6")
}Screen Objects with Kakao DSL
Kakao uses a Screen abstraction — define your screen's interactive elements once, reuse across tests.
object LoginScreen : Screen<LoginScreen>() {
val emailField = KEditText { withId(R.id.email) }
val passwordField = KEditText { withId(R.id.password) }
val loginButton = KButton { withId(R.id.login_button) }
val errorMessage = KTextView { withId(R.id.error_text) }
}This is cleaner than Espresso's onView(withId(...)) scattered through test methods.
Writing a Kaspresso Test
class LoginTest : TestCase() {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Test
fun loginWithValidCredentials() = run {
step("Enter credentials") {
LoginScreen {
emailField.typeText("user@example.com")
passwordField.typeText("password123")
loginButton.click()
}
}
step("Verify dashboard is shown") {
DashboardScreen {
welcomeText.isVisible()
welcomeText.hasText("Welcome back")
}
}
}
}step() blocks organize test execution into named phases. These map directly to Allure report steps.
Flakiness Prevention
Kaspresso wraps every interaction in an automatic retry loop with configurable timeout. By default, it retries UI interactions for up to 2 seconds before failing.
You can tune this globally:
class MyTest : TestCase(
kaspressoBuilder = Kaspresso.Builder.simple {
flakySafetyParams = FlakySafetyParams.simple(
timeoutMs = 5000,
intervalMs = 500
)
}
)For specific views that are known to be slow:
flakySafely(timeoutMs = 10000) {
MyScreen.lazyLoadedItem.isVisible()
}Allure Integration
Kaspresso has built-in Allure support. Steps, screenshots, and logs are automatically captured.
Change the test runner in build.gradle.kts:
android {
defaultConfig {
testInstrumentationRunner = "com.kaspersky.android-components.kaspresso.alluresupport.AllureAndroidJUnitRunner"
}
}Run tests and pull results:
./gradlew connectedAndroidTest
adb pull /sdcard/allure-results ./allure-results
allure serve ./allure-resultsThe report shows each step() with screenshots on failure, device info, and timing.
ADB Server Mode
Kaspresso can communicate with the host machine via ADB during tests. This enables advanced scenarios like:
- Simulating network conditions
- Toggling device settings (airplane mode, GPS, notifications)
- Uploading files to the device mid-test
Enable it in your test configuration:
class NetworkTest : TestCase(
kaspressoBuilder = Kaspresso.Builder.simple {
adbServer = AdbServerImpl(adbServerPort = AdbServer.DEFAULT_PORT)
}
) {
@Test
fun testOfflineMode() = run {
step("Enable airplane mode") {
device.network.toggleAirplaneMode(true)
}
step("Verify offline message") {
HomeScreen.offlineMessage.isVisible()
}
step("Restore connectivity") {
device.network.toggleAirplaneMode(false)
}
}
}ADB server requires the adbserver-desktop.jar running on the host during test execution.
Interceptors
Kaspresso supports interceptors — hooks that fire before and after every UI interaction. Use them for custom logging, screenshots on every step, or additional assertions.
Kaspresso.Builder.simple {
viewInterceptors += object : ViewInteractionInterceptor {
override fun onAll(interaction: ViewInteraction) {
Allure.addAttachment("screenshot", takeScreenshot())
}
}
}Comparing to Espresso
| Feature | Espresso | Kaspresso |
|---|---|---|
| DSL readability | Low — verbose matcher chains | High — Kakao screen objects |
| Flakiness handling | Manual waits | Built-in retry |
| Screenshots on failure | None by default | Automatic with Allure |
| Step reporting | None | Named steps in reports |
| UIAutomator support | Separate boilerplate | Integrated |
What to Test with Kaspresso
Kaspresso is best for:
- Login / auth flows — multiple screens, form validation, error states
- Onboarding sequences — multi-step with state transitions
- Settings screens — toggles, pickers, navigation
- Any test you've had to add
Thread.sleep()to — replace withflakySafely
For unit-layer tests or Compose UI, use other tools (Robolectric or Compose testing APIs). Kaspresso targets View-based UI on real devices or emulators.
Summary
Kaspresso makes Android UI tests maintainable. The Kakao DSL reduces noise in test code, the retry logic eliminates most flakiness, and Allure integration gives you readable failure reports without extra tooling. If you're writing Espresso tests today and they're fragile or hard to read, migrating to Kaspresso is a straightforward upgrade.