mcpField
E
espresso-skill

espresso-skill

github.com/lambdatestTesting & quality384226 installsUpdated 2w ago
Repository

About espresso-skill

espresso-skill is a reference skill for Android UI testing with Espresso, aimed at QA engineers and Android developers. It provides Kotlin test patterns for matching, interacting with, and asserting views, plus local and TestMu AI cloud execution guidance.

Key features

  1. 01Kotlin Espresso test patterns
  2. 02View matchers, actions, and assertions
  3. 03Idling Resources for asynchronous UI
  4. 04Intent and RecyclerView testing
  5. 05Local and cloud execution guidance

Use cases

  1. 01Test Android login screens and controls
  2. 02Check UI behavior during asynchronous operations
  3. 03Verify intents and RecyclerView interactions
  4. 04Run instrumentation tests on local devices or cloud

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/lambdatest/agent-skills --skill espresso-skill

After installing, type/espresso-skillin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

reference/
advanced-patterns.md
cloud-integration.md
playbook.md

Details

From the project README

Espresso Automation Skill

You are a senior Android QA engineer specializing in Espresso UI testing.

Step 1 — Execution Target

├─ Mentions "cloud", "TestMu", "LambdaTest", "device farm"?
│  └─ TestMu AI cloud (upload APK + test APK)
│
├─ Mentions "emulator", "local", "connected device"?
│  └─ Local: ./gradlew connectedAndroidTest
│
└─ Default → Local emulator

Core Patterns — Kotlin (Default)

Basic Test
@RunWith(AndroidJUnit4::class)
class LoginTest {

    @get:Rule
    val activityRule = ActivityScenarioRule(LoginActivity::class.java)

    @Test
    fun loginWithValidCredentials() {
        // Type email
        onView(withId(R.id.emailInput))
            .perform(typeText("[email protected]"), closeSoftKeyboard())

        // Type password
        onView(withId(R.id.passwordInput))
            .perform(typeText("password123"), closeSoftKeyboard())

        // Click login button
        onView(withId(R.id.loginButton))
            .perform(click())

        // Verify dashboard is displayed
        onView(withId(R.id.dashboardTitle))
            .check(matches(isDisplayed()))
            .check(matches(withText("Welcome")))
    }

    @Test
    fun loginWithInvalidCredentials_showsError() {
        onView(withId(R.id.emailInput))
            .perform(typeText("[email protected]"), closeSoftKeyboard())
        onView(withId(R.id.passwordInput))
            .perform(typeText("wrong"), closeSoftKeyboard())
        onView(withId(R.id.loginButton))
            .perform(click())
        onView(withId(R.id.errorText))
            .check(matches(isDisplayed()))
            .check(matches(withText(containsString("Invalid"))))
    }
}
ViewMatchers (Finding Elements)
// By ID (best)
onView(withId(R.id.loginButton))

// By text
onView(withText("Login"))

// By content description (accessibility)
onView(withContentDescription("Submit form"))

// By hint text
onView(withHint("Enter your email"))

// Combined matchers
onView(allOf(withId(R.id.button), withText("Submit"), isDisplayed()))

// In RecyclerView
onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions.actionOnItemAtPosition<ViewHolder>(0, click()))

// By parent
onView(allOf(withText("Delete"), isDescendantOfA(withId(R.id.toolbar))))
ViewActions (Performing Actions)
.perform(click())                          // Tap
.perform(longClick())                      // Long press
.perform(typeText("hello"))                // Type text
.perform(replaceText("new text"))          // Replace text
.perform(clearText())                      // Clear field
.perform(closeSoftKeyboard())              // Dismiss keyboard
.perform(scrollTo())                       // Scroll to element
.perform(swipeUp())                        // Swipe gesture
.perform(swipeDown())
.perform(swipeLeft())
.perform(swipeRight())
.perform(pressBack())                      // Back button
ViewAssertions (Checking State)
.check(matches(isDisplayed()))             // Visible
.check(matches(not(isDisplayed())))        // Not visible
.check(matches(withText("Expected")))      // Text matches
.check(matches(isEnabled()))               // Enabled
.check(matches(isChecked()))               // Checkbox checked
.check(matches(hasErrorText("Required")))  // Error text
.check(doesNotExist())                     // Not in hierarchy
Idling Resources (Async Operations)
// Register before test
@Before
fun setUp() {
    IdlingRegistry.getInstance().register(myIdlingResource)
}

// Unregister after test
@After
fun tearDown() {
    IdlingRegistry.getInstance().unregister(myIdlingResource)
}

// Custom IdlingResource for network calls
class NetworkIdlingResource : IdlingResource {
    private var callback: IdlingResource.ResourceCallback? = null
    private var isIdle = true

    override fun getName() = "NetworkIdlingResource"
    override fun isIdleNow() = isIdle
    override fun registerIdleTransitionCallback(callback: ResourceCallback) {
        this.callback = callback
    }

    fun setIdle(idle: Boolean) {
        isIdle = idle
        if (idle) callback?.onTransitionToIdle()
    }
}
Anti-Patterns
BadGoodWhy
Thread.sleep()IdlingResourcesEspresso auto-syncs UI thread
XPath-like traversalwithId(R.id.x)Direct ID is fastest
Testing across activitiesTest single screen, mock dataIsolation
No closeSoftKeyboard()Always close after typeText()Keyboard blocks elements
TestMu AI Cloud
# 1. Build APK and test APK
./gradlew assembleDebug assembleDebugAndroidTest

# 2. Upload both to LambdaTest
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
  -X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
  -F "appFile=@app/build/outputs/apk/debug/app-debug.apk" \
  -F "type=android"

curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
  -X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
  -F "appFile=@app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk" \
  -F "type=android"

# 3. Execute on real devices via API
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
  -X POST "https://mobile-api.lambdatest.com/framework/v1/espresso/build" \
  -H "Content-Type: application/json" \
  -d '{
    "app": "lt://APP123",
    "testSuite": "lt://TEST456",
    "device": ["Pixel 8-14", "Galaxy S24-14"],
    "build": "Espresso Cloud Build",
    "video": true, "deviceLog": true
  }'

build.gradle Setup

android {
    defaultConfig {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.1'
    androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
    androidTestImplementation 'androidx.test:runner:1.5.2'
    androidTestImplementation 'androidx.test:rules:1.5.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
}

Quick Reference

TaskCommand/Code
Run all tests./gradlew connectedAndroidTest
Run specific class./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.LoginTest
Run on specific device./gradlew connectedAndroidTest -PtestDevice=emulator-5554
Intent verificationIntents.init() → intended(hasComponent(...)) → Intents.release()
RecyclerView scrollRecyclerViewActions.scrollToPosition<>(10)
ScreenshotScreenshot.capture(activityRule.activity)

Reference Files

FileWhen to Read
reference/cloud-integration.mdLambdaTest Espresso, device farm, API
reference/advanced-patterns.mdIntents, RecyclerView, custom matchers

Deep Patterns → reference/playbook.md

§SectionLines
1Project SetupGradle deps, Orchestrator
2Test Structure & LifecycleRules, permissions, annotations
3Custom Matchers & ViewActionsRecyclerView, wait, scroll
4RecyclerView TestingScroll, click child, swipe, assert
5Idling ResourcesCounting, OkHttp, custom
6Intent TestingShare, stub, camera
7MockWebServer for API TestsEnqueue, error handling
8CI/CD IntegrationGitHub Actions, emulator runner
9Debugging Quick-Reference10 common problems
10Best Practices Checklist13 items

Comments

Markdown supported