Skip to content
Peenaka Wedamulla

All Publications & Field Notes

12 August 20248 min readVerified Engineering Publication

I Got Tired of Rebuilding the Same Android Test Framework from Scratch

An architectural blueprint for a reusable Android test harness with Espresso and Appium—solving device fragmentation, idling resources, flaky gestures, and parallelized cloud test matrix runs.

  • Android Testing
  • Espresso
  • Appium
  • Mobile Automation
  • Test Architecture
I Got Tired of Rebuilding the Same Android Test Framework from Scratch

Syndicated Reading Channels

Read, share, and discuss this article on LinkedIn Pulse or Medium

The Mobile QA Groundhog Day

Starting a new mobile automation project often feels like Groundhog Day. You spend weeks configuring the same boilerplate:

  • Setting up Gradle test runners and ADB device orchestration.
  • Wrangling flaky async UI synchronizations and IdlingResource hooks.
  • Handling permissions dialogs, OEM keyboard quirks, and varying Android SDK versions.
  • Writing brittle XPath locators that break every time a designer tweaks an elevation or padding.

After building mobile automation suites across multiple FinTech and enterprise products, I decided to distill these patterns into a modular, reusable Android Test Architecture.

Architectural Tenets of a Resilient Mobile Framework

  1. Hybrid Strategy: Espresso for Speed, Appium for End-to-End:

    • Espresso runs inside the application process for ultra-fast component and flow verification (under 2 seconds per test).
    • Appium is reserved for cross-app scenarios: SMS OTP auto-fill verification, biometric prompt fallbacks, and push notification tray interactions.
  2. Custom ViewMatchers & Smart Synchronization: Never use hardcoded Thread.sleep(). By leveraging custom Espresso matchers and AndroidX Test CountingIdlingResource, tests pause only when network requests or OkHttp dispatchers are actively processing.

// Custom Espresso Action with Smart Retry & Auto-Scroll
fun clickOnVisible(viewMatcher: Matcher<View>): ViewAction {
    return object : ViewAction {
        override fun getConstraints(): Matcher<View> = isAssignableFrom(View::class.java)
        override fun getDescription(): String = "click on view when visible and idle"
        override fun perform(uiController: UiController, view: View) {
            uiController.loopMainThreadUntilIdle()
            if (!view.isShown) {
                ViewActions.scrollTo().perform(uiController, view)
            }
            view.performClick()
        }
    }
}
  1. Cloud Device Farm Parallelism:
    • Designed to run seamlessly in GitHub Actions with emulator matrix jobs, or dispatched to AWS Device Farm and BrowserStack.
    • Automatic video recording, logcat dumping, and failure screenshot capture attached directly to CI test runs.

The Result

By eliminating boilerplate and enforcing strict separation of concerns (Screen Object Model + Fluent Assertions), our mobile regression execution time dropped by 70%, while flakiness plummeted to under 0.5% across 20+ distinct Android device configurations.

PW

Peenaka Wedamulla

SDET / QA Engineer

QA Automation Engineer specializing in scalable test automation across web, mobile, and API platforms in FinTech. Focused on building quality from the start rather than testing it at the end.

Discussion & Feedback

Have thoughts on this test strategy? Share your experience or questions below.

Top 3 Verified Community Comments

Top Discussion Insights (0 comments)

JSON Persisted

No comments yet. Be the first to start the discussion below!

Add to the Discussion

Your perspective will be verified and published to the live community discussion.