Agent skill

Writing Espresso Tests

by skydoves in skydoves/android-testing-skills

A skill your agent uses to write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...)), onData(...) for AdapterView, RecyclerViewActions…

Apache-2.0Auto-check passedMobile

Install Writing Espresso Tests

skills CLI
$ npx skills add skydoves/android-testing-skills --skill writing-espresso-tests -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install skydoves/android-testing-skills writing-espresso-tests --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/skydoves/android-testing-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/instrumentation/espresso/writing-espresso-tests .claude/skills/writing-espresso-tests && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
writing-espresso-tests
GitHub stars
333
Token cost
~5.4k tokens
SKILL.md length
1,224 words
Files
1
Skills in repo
50
Repo updated
First seen
Licence
Apache-2.0

At a glance

A skill your agent uses to write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...)), onData(...) for AdapterView, RecyclerViewActions…

  • Works in 6 steps: Submits the action on the main thread… → Calls… → Resolves the view (viewMatcher against… → …
  • Write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...))
  • SKILL.md covers When to use this skill, When NOT to use this skill, Prerequisites and Workflow, plus 4 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Writing Espresso Tests is an agent skill from skydoves/android-testing-skills. Use this skill to write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...)), onData(...) for AdapterView, RecyclerViewActions, Intents.intended/intending, and IdlingResource registration. Covers the full ViewMatchers / ViewActions / ViewAssertions catalog, the RootMatchers.DEFAULT exclusion of dialogs and popups (the inRoot(isDialog()) requirement), the BottomNavigationViewActions doesn't-exist gotcha in 3.7.0 contrib, and the PickerActions.setDate(year, month, day)…

Its SKILL.md is about 5.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Mobile, covering Mobile testing and debugging. It works with Android. The repository describes itself as: ⚡️ A set of skills for Android testing: Compose UI, AndroidX Test, JVM unit tests, and ADB. The licence is Apache-2.0.

When your agent uses it

  • Write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...))
  • OnData(...) for AdapterView
  • RecyclerViewActions
  • Intents.intended/intending

Example prompts

  • “how do I click in a dialog”
  • “Espresso typeText doesn”
  • “intent stub for camera”
  • “/writing-espresso-tests”

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. Submits the action on the main thread via the dagger-injected mainThreadExecutor.
  2. Calls uiController.loopMainThreadUntilIdle() until: the main MessageQueue is empty, all IdlingResources registered with IdlingRegistry…
  3. Resolves the view (viewMatcher against the current Root's hierarchy). 0 → NoMatchingViewException; 2+ → AmbiguousViewMatcherException.
  4. Checks viewAction.getConstraints().matches(view) → throws PerformException if false.
  5. Calls viewAction.perform(uiController, view) (or viewAssertion.check(view, null)).
  6. Loops the main thread to idle again before returning.

What it can do on your machine

Read from SKILL.md and the folder at commit 8665ed5. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • developer.android.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Writing Espresso Tests loads about 5.4k tokens when it runs. Until then it costs about 205 tokens; SKILL.md has 1,224 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~205
When it runs · the whole SKILL.md, loaded when a task matches
~5.4k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from skydoves/android-testing-skills at commit 8665ed5, republished under its Apache-2.0 licence (© skydoves). 1,224 words, ~5,381 tokens.

Download SKILL.mdSave it as .claude/skills/writing-espresso-tests/SKILL.md (or your agent's skills folder).
name
writing-espresso-tests
description
Use this skill to write Espresso 3.7.0 tests against Android Views — `onView(matcher).perform(action).check(matches(...))`, `onData(...)` for `AdapterView`, `RecyclerViewActions`, `Intents.intended/intending`, and `IdlingResource` registration. Covers the full `ViewMatchers` / `ViewActions` / `ViewAssertions` catalog, the `RootMatchers.DEFAULT` exclusion of dialogs and popups (the `inRoot(isDialog())` requirement), the `BottomNavigationViewActions` doesn't-exist gotcha in 3.7.0 contrib, and the `PickerActions.setDate(year, month, day)` 1-12 month convention. Use when the user reports `NoMatchingViewException`, `AmbiguousViewMatcherException`, `PerformException`, asks "how do I click in a dialog", "Espresso typeText doesn't fire", "intent stub for camera", or "Espresso vs UiAutomator".
license
Apache-2.0. See LICENSE for complete terms.
metadata.author
Jaewoong Eum (skydoves)
metadata.keywords
espresso, onView, ViewMatchers, ViewActions, ViewAssertions, RootMatchers, IdlingResource, IdlingPolicies, RecyclerViewActions, Intents, PickerActions…

Writing Espresso Tests — The Idle-Then-Act Model for Android Views

Espresso is the canonical instrumentation-time UI test framework for Android Views (not Compose — for Compose, use the compose/ skill set; for cross-Compose-View interop see ../../../compose/interop/testing-with-espresso-interop/SKILL.md). Each onView(...).perform(...).check(...) call internally pumps the main looper to idle, finds exactly one matching view, runs the action, and pumps to idle again before checking. This skill encodes the full matcher/action/assertion catalog, the RootMatchers.DEFAULT trap, and the per-version gotchas.

When to use this skill

  • The user is testing an Activity, Fragment, or Dialog whose UI is built with Android Views (XML layouts, AppCompat, Material View components).
  • A test fails with NoMatchingViewException, AmbiguousViewMatcherException, or PerformException.
  • A click on an AlertDialog button silently does nothing — RootMatchers.DEFAULT excludes dialogs.
  • The user wants to stub Intent.startActivityForResult for camera, contacts, or share flows.
  • The user wants to register an IdlingResource for a long-running task that Espresso can't see.
  • The user is debugging a RecyclerView interaction.
  • The user mentions BottomNavigationViewActions and is surprised it isn't on the classpath in Espresso 3.7.0 contrib.

When NOT to use this skill

  • The UI is Jetpack Compose — use compose/ skills (../../../compose/finders/finding-nodes-by-tag-text-content/SKILL.md, etc.). Mixed Compose+View hosts: ../../../compose/interop/testing-with-espresso-interop/SKILL.md.
  • The test crosses app boundaries (Settings, dialer, system UI) — InjectEventSecurityException from Espresso. Use ../../uiautomator/cross-app-tests-with-uiautomator/SKILL.md.
  • The runner stack is not yet set up — start with ../../runner/running-instrumented-tests-with-androidjunit4/SKILL.md.
  • The Activity has not been launched — pair with ../../scenarios/launching-activities-with-activityscenario/SKILL.md.

Prerequisites

  • Runner stack from ../../runner/running-instrumented-tests-with-androidjunit4/SKILL.md.
  • Espresso artifacts (3.7.0 GA per docs/CORPUS.md H.1):
kotlin
dependencies {
    androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
    androidTestImplementation("androidx.test.espresso:espresso-contrib:3.7.0")     // RecyclerViewActions, PickerActions
    androidTestImplementation("androidx.test.espresso:espresso-intents:3.7.0")     // Intents.intended/intending
    androidTestImplementation("androidx.test.espresso:espresso-accessibility:3.7.0")
    androidTestImplementation("androidx.test.espresso:espresso-web:3.7.0")         // WebView
    implementation("androidx.test.espresso:espresso-idling-resource:3.7.0")        // for production code that exposes IdlingResources
}

android.testOptions.animationsDisabled = true   // hermetic
  • An Activity (or Fragment) launched via ActivityScenario / ActivityScenarioRule / FragmentScenario.

Workflow

  • 1. Find views with onView(matcher). The matcher is a Hamcrest Matcher<View>. Identity-first matchers (most preferred): withId(R.id.button), withTagKey(int), withResourceName(String). Text/desc fallback: withText, withContentDescription, withHint. Source: androidx.test.espresso.matcher.ViewMatchers (lines 79-1000+, R3 lines 180-280):
kotlin
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.*
import org.hamcrest.Matchers.allOf

onView(withId(R.id.submit_button))
onView(allOf(withText("Submit"), isEnabled()))
onView(withContentDescription("Open menu"))
onView(allOf(withId(R.id.subtitle), hasDescendant(withText("Hello"))))
  • 2. Compose state-aware matchers. Common state matchers from ViewMatchers: isDisplayed, isCompletelyDisplayed, isDisplayingAtLeast(percent), isEnabled/isNotEnabled, isFocused, isChecked/isNotChecked, isSelected, withEffectiveVisibility(Visibility.VISIBLE|INVISIBLE|GONE), withAlpha(float). Hierarchy matchers: withParent, hasDescendant, withChild, isRoot, withClassName, isAssignableFrom(Class). Layout-relative: hasSibling, isDescendantOfA. Source: R3 lines 180-280.

  • 3. Act with ViewActions via .perform(action). From androidx.test.espresso.action.ViewActions:

kotlin
import androidx.test.espresso.action.ViewActions.*

onView(withId(R.id.email)).perform(typeText("test@example.com"), closeSoftKeyboard())
onView(withId(R.id.submit_button)).perform(click())
onView(withId(R.id.input)).perform(replaceText("new"), pressImeActionButton())
onView(withId(R.id.list)).perform(swipeUp())
onView(withId(R.id.scroller)).perform(scrollTo())   // requires single-direction ScrollView

Click family: click, longClick, doubleClick. Text: typeText, replaceText, clearText, pressKey, pressImeActionButton, closeSoftKeyboard. Gesture: swipeUp, swipeDown, swipeLeft, swipeRight, scrollTo. Source: androidx.test.espresso.action.ViewActions.

  • 4. Assert with ViewAssertions via .check(...). From androidx.test.espresso.assertion.ViewAssertions:
kotlin
import androidx.test.espresso.assertion.ViewAssertions.*
import androidx.test.espresso.matcher.ViewMatchers.*

onView(withId(R.id.title)).check(matches(withText("Welcome")))
onView(withId(R.id.banner)).check(matches(isDisplayed()))
onView(withId(R.id.banner)).check(doesNotExist())
onView(withId(R.id.list)).check(selectedDescendantsMatch(withId(R.id.row), isCompletelyDisplayed()))

matches(matcher) passes when the resolved view matches; doesNotExist() passes when the matcher resolved zero views (NOT when one was found and is invisible — use matches(not(isDisplayed())) for that). Source: R3 lines 290-311.

  • 5. Recognize the implicit idle. Every onView(...).perform(...) and .check(...) call internally:
    1. Submits the action on the main thread via the dagger-injected mainThreadExecutor.
    2. Calls uiController.loopMainThreadUntilIdle() until: the main MessageQueue is empty, all IdlingResources registered with IdlingRegistry report isIdleNow == true, all registered non-main Loopers are idle, and the AsyncTask thread pool is idle.
    3. Resolves the view (viewMatcher against the current Root's hierarchy). 0 → NoMatchingViewException; 2+ → AmbiguousViewMatcherException.
    4. Checks viewAction.getConstraints().matches(view) → throws PerformException if false.
    5. Calls viewAction.perform(uiController, view) (or viewAssertion.check(view, null)).
    6. Loops the main thread to idle again before returning.

This is the idle-then-act model. Tests that work locally but fail on CI are usually missing an IdlingResource for a non-Looper-driven async path (custom ExecutorService, OkHttp's dispatcher, etc.). Source: R3 lines 165-178, 811-820.

  • 6. For dialogs and popups, override the root with inRoot(...). RootMatchers.DEFAULT excludes dialogs, popups, and toasts (it matches "the focused activity window"). For an AlertDialog button:
kotlin
import androidx.test.espresso.matcher.RootMatchers.*

onView(withText("Submit")).inRoot(isDialog()).perform(click())
onView(withText("Copy")).inRoot(isPlatformPopup()).perform(click())   // overflow menu, popup window
onView(withText("Saved")).inRoot(isPlatformPopup()).check(matches(isDisplayed()))   // toast

RootMatchers.DEFAULT is allOf(hasWindowLayoutParams(), allOf(anyOf(allOf(isDialog(), withDecorView(hasWindowFocus())), isSubwindowOfCurrentActivity()), isFocusable())) per R3 line 311 — that's why a generic dialog button click silently misses.

  • 7. For RecyclerView, use RecyclerViewActions from contrib. onView(...).perform(...) against a RecyclerView's row directly does NOT work because the row may be off-screen (no view to find):
kotlin
import androidx.test.espresso.contrib.RecyclerViewActions.*

onView(withId(R.id.list))
    .perform(scrollToPosition<RecyclerView.ViewHolder>(20))

onView(withId(R.id.list))
    .perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(3, click()))

onView(withId(R.id.list))
    .perform(scrollTo<RecyclerView.ViewHolder>(hasDescendant(withText("Pikachu"))))

onView(withId(R.id.list))
    .perform(actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText("Pikachu")), click()))

All RecyclerViewActions require isAssignableFrom(RecyclerView.class) AND isDisplayed() constraint (R3 line 428).

  • 8. For AdapterView (ListView, Spinner), use onData(...) not onView(...).
kotlin
import androidx.test.espresso.Espresso.onData
import org.hamcrest.Matchers.allOf

onData(allOf(`is`(instanceOf(String::class.java)), `is`("Item 5")))
    .inAdapterView(withId(R.id.list))
    .perform(click())

onData scrolls the adapter to bring the item on-screen before resolving its view.

  • 9. Stub and verify intents with Intents. Init in @Before, release in @After. The IntentsRule (3.5+) handles both:
kotlin
import androidx.test.espresso.intent.rule.IntentsRule
import androidx.test.espresso.intent.Intents.*
import androidx.test.espresso.intent.matcher.IntentMatchers.*

@get:Rule val intentsRule = IntentsRule()

@Test
fun openCameraStub() {
    intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE))
        .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, Intent()))

    onView(withId(R.id.take_photo)).perform(click())

    intended(allOf(
        hasAction(MediaStore.ACTION_IMAGE_CAPTURE),
        hasFlag(Intent.FLAG_GRANT_READ_URI_PERMISSION),
    ))
}

IntentsTestRule is @Deprecated per docs/CORPUS.md H.7 — use IntentsRule or call Intents.init() / Intents.release() manually.

  • 10. Register IdlingResource for non-Looper async work. Espresso watches the main looper and the AsyncTask pool by default; for OkHttp, custom ExecutorService, or sensor pipelines you must surface idleness:
kotlin
class CountingIdlingResource(private val name: String) : IdlingResource {
    private val counter = AtomicInteger(0)
    private var callback: IdlingResource.ResourceCallback? = null

    override fun getName() = name
    override fun isIdleNow() = counter.get() == 0
    override fun registerIdleTransitionCallback(cb: IdlingResource.ResourceCallback) { callback = cb }

    fun increment() { counter.incrementAndGet() }
    fun decrement() { if (counter.decrementAndGet() == 0) callback?.onTransitionToIdle() }
}

@Before fun setUp() = IdlingRegistry.getInstance().register(myIdler)
@After fun tearDown() = IdlingRegistry.getInstance().unregister(myIdler)

// Master policy default is 60 seconds; setIdlingResourceTimeout default is 26 seconds.
// For debugger sessions, opt out of timeouts entirely with
//   IdlingPolicies.setMasterPolicyTimeoutWhenDebuggerAttached(false)
// — there is no "-1 = infinite" magic value.
IdlingPolicies.setMasterPolicyTimeout(60, TimeUnit.SECONDS)
IdlingPolicies.setIdlingResourceTimeout(45, TimeUnit.SECONDS)

Wrap network calls / async work in increment() / decrement() from production code (the :espresso-idling-resource artifact is on implementation, not androidTestImplementation).

  • 11. Read errors precisely.

    • NoMatchingViewException — the matcher resolved zero views in the current root. Confirm the view is currently displayed; if it is in a dialog, add .inRoot(isDialog()).
    • AmbiguousViewMatcherException — 2+ views matched. Tighten with allOf(...).
    • PerformException — the action's constraint failed (e.g. click() requires isDisplayingAtLeast(90)). The exception message includes the constraint description.
    • InjectEventSecurityException — an event would cross to a foreign window. The test reached system UI. Use UiAutomator instead.
Show full SKILL.md (473 more words)Show less

Patterns

Pattern: WRONG vs RIGHT — clicking a dialog button
kotlin
// WRONG
onView(withText("Submit")).perform(click())
// WRONG because: RootMatchers.DEFAULT (line 51 of RootMatchers.java) excludes dialogs.
// The matcher does not see the dialog window. Result: NoMatchingViewException ("No views in
// hierarchy found matching: with text Submit"), even though the dialog is clearly on screen.
kotlin
// RIGHT
import androidx.test.espresso.matcher.RootMatchers.isDialog
onView(withText("Submit")).inRoot(isDialog()).perform(click())

Same applies to popup menus (isPlatformPopup()) and toasts.

Pattern: WRONG vs RIGHT — BottomNavigationView interaction (Espresso 3.7.0 contrib)
kotlin
// WRONG
import androidx.test.espresso.contrib.BottomNavigationViewActions
onView(withId(R.id.bottom_nav)).perform(BottomNavigationViewActions.selectItem(R.id.tab_profile))
// WRONG because: BottomNavigationViewActions does NOT exist in espresso-contrib 3.7.0.
// The class was historically documented but never shipped in 3.7.0's contrib jar (R3 lines 91-93).
// Compile fails with "unresolved reference".
kotlin
// RIGHT — click the underlying menu item view directly
import com.google.android.material.bottomnavigation.BottomNavigationView
import org.hamcrest.Matchers.allOf

onView(allOf(
    withId(R.id.tab_profile),
    isDescendantOfA(isAssignableFrom(BottomNavigationView::class.java)),
)).perform(click())
Pattern: WRONG vs RIGHT — PickerActions.setDate month convention
kotlin
// WRONG
import androidx.test.espresso.contrib.PickerActions
onView(withClassName(equalTo(DatePicker::class.java.name)))
    .perform(PickerActions.setDate(2026, 4, 15))   // intends "May 15, 2026"
// WRONG because: PickerActions.setDate uses 1-12 month convention (it subtracts 1 internally
// before calling DatePicker.updateDate which uses 0-11). Passing 4 sets April, not May.
// Source: R3 line 460, PickerActions.java line 49.
kotlin
// RIGHT — May = 5
onView(withClassName(equalTo(DatePicker::class.java.name)))
    .perform(PickerActions.setDate(2026, 5, 15))
Pattern: WRONG vs RIGHT — Thread.sleep to wait for an animation
kotlin
// WRONG
onView(withId(R.id.fab)).perform(click())
Thread.sleep(500)            // wait for the FAB exit animation
onView(withId(R.id.fab)).check(doesNotExist())
// WRONG because: Thread.sleep is a smell. Espresso's idle pump already waits for animations
// driven through the main looper; if it doesn't, register an IdlingResource for the offending
// async path. See `docs/CORPUS.md` D.7 — "Thread.sleep is a smell".
kotlin
// RIGHT — disable animations OR register an IdlingResource
android.testOptions.animationsDisabled = true     // for property animators driven via Choreographer
// And/or, for non-Looper waits:
IdlingPolicies.setMasterPolicyTimeout(45, TimeUnit.SECONDS)
IdlingRegistry.getInstance().register(myAnimationIdler)
Pattern: scrolling a RecyclerView to an off-screen row before clicking it
kotlin
import androidx.test.espresso.contrib.RecyclerViewActions.*
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant

onView(withId(R.id.list))
    .perform(scrollTo<RecyclerView.ViewHolder>(hasDescendant(withText("Snorlax"))))
onView(withId(R.id.list))
    .perform(actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText("Snorlax")), click()))

scrollTo and actionOnItem use a HOLDER matcher (matches the underlying RecyclerView.ViewHolder's itemView), not a Matcher<View> against the screen.

Mandatory rules

  • MUST add .inRoot(isDialog()) for any matcher targeting an AlertDialog content view, .inRoot(isPlatformPopup()) for popup menus and toasts. RootMatchers.DEFAULT excludes them.
  • MUST prefer withId / withTagKey / withResourceName over withText. Text matchers are i18n-fragile and churn with copy edits.
  • MUST NOT rely on Thread.sleep to wait for animations or async work. Use IdlingResource + IdlingPolicies.setMasterPolicyTimeout, and testOptions.animationsDisabled = true.
  • MUST NOT import androidx.test.espresso.contrib.BottomNavigationViewActions — the class is not in espresso-contrib:3.7.0. Use the isDescendantOfA(isAssignableFrom(BottomNavigationView::class.java)) workaround.
  • MUST pass 1-12 (not 0-11) for the month parameter of PickerActions.setDate(year, month, day). The action subtracts 1 internally before calling DatePicker.updateDate.
  • MUST call Intents.init() / Intents.release() (or use IntentsRule) — intended / intending only work between those calls.
  • MUST put :espresso-idling-resource:3.7.0 on implementation (production), not androidTestImplementation. The production code is what increments / decrements the resource.
  • MUST NOT use IntentsTestRule — it is @Deprecated. Use IntentsRule (3.5+) or manual init/release.
  • PREFERRED: disable animations globally via testOptions.animationsDisabled = true; do not toggle Settings.Global flags by hand.
  • PREFERRED: read every NoMatchingViewException message — it includes a hierarchy dump. The view you wanted may be present but in a different Root.

Verification

  • grep -r "Thread.sleep" src/androidTest/ returns nothing (or only screenshot-test files where the RenderThread is the wait target).
  • grep -r "BottomNavigationViewActions" src/androidTest/ returns nothing.
  • grep -r "IntentsTestRule" src/androidTest/ returns nothing — use IntentsRule or manual init/release.
  • Every inRoot(isDialog()) matches an AlertDialog-class API in production; otherwise the test passes against the wrong root.
  • ./gradlew :<module>:connectedDebugAndroidTest runs without NoMatchingViewException or PerformException from the catalog above.
  • testOptions.animationsDisabled = true is set in the module's android block.
  • All registered IdlingResources call IdlingRegistry.getInstance().unregister(...) in @After (or use a JUnit rule).

References

  • Android Developers — Espresso overview: https://developer.android.com/training/testing/espresso
  • Android Developers — Espresso cheat sheet (matchers, actions, assertions): https://developer.android.com/training/testing/espresso/cheat-sheet
  • Android Developers — IdlingResource guide: https://developer.android.com/training/testing/espresso/idling-resource
  • AndroidX Test (Espresso) release notes: https://developer.android.com/jetpack/androidx/releases/test
  • androidx/test/espresso/matcher/ViewMatchers.java (lines 79-1000+) — full matcher catalog. Identity (withId, withTagKey, withResourceName); text (withText, withSubstring, withHint); state (isDisplayed, isEnabled, isChecked, withEffectiveVisibility); hierarchy (withParent, hasDescendant, withChild, isRoot, isDescendantOfA).
  • androidx/test/espresso/matcher/RootMatchers.java line 51 — DEFAULT = allOf(hasWindowLayoutParams(), allOf(anyOf(allOf(isDialog(), withDecorView(hasWindowFocus())), isSubwindowOfCurrentActivity()), isFocusable())). The trap.
  • androidx/test/espresso/action/ViewActions.java — click, longClick, doubleClick, typeText, replaceText, clearText, pressKey, swipeUp/Down/Left/Right, scrollTo, pressImeActionButton, closeSoftKeyboard.
  • androidx/test/espresso/assertion/ViewAssertions.java lines 51-118 — matches, doesNotExist, selectedDescendantsMatch.
  • androidx/test/espresso/contrib/RecyclerViewActions.java lines 200-249 — scrollToPosition, scrollTo, actionOnItemAtPosition, actionOnItem. Constraint: isAssignableFrom(RecyclerView) AND isDisplayed.
  • androidx/test/espresso/contrib/PickerActions.java lines 36-100 — setDate(year, monthOfYear, dayOfMonth) with monthOfYear 1-12 (line 49 KDoc). setTime(hours, minutes).
  • androidx/test/espresso/IdlingPolicies.java lines 96-112 — setMasterPolicyTimeout, setIdlingResourceTimeout, setMasterPolicyTimeoutWhenDebuggerAttached.
  • androidx/test/espresso/intent/Intents.java — init, release, intended, intending. androidx.test.espresso.intent.rule.IntentsRule is the modern JUnit4 wrapper.
  • tasks/research/R3-espresso.md — full Espresso 3.7.0 deep-dive. Lines 91-93 (BottomNavigationViewActions absent), 311 (RootMatchers.DEFAULT), 456-460 (PickerActions month convention), 811-820 (idle-then-act).
  • docs/CORPUS.md Section H.5 — Espresso essentials and RootMatchers.DEFAULT exclusions.

© skydoves, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in instrumentation/espresso/writing-espresso-tests of skydoves/android-testing-skills.

Open the folder on GitHubat commit 8665ed5

Compare with similar skills

Writing Espresso Tests next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Writing Espresso Tests compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Writing Espresso Tests this skillskydoves/android-testing-skills333—~5.4kAutomated safety check: PassApache-2.0
Phone HarnessShawnPana/phone-harness3.2k—~4.3kAutomated safety check: PassMIT
Mobile QAtloncorp/tlon-apps107—~2.4kAutomated safety check: PassMIT
Androidyang1ming/android-harness176—~259Automated safety check: PassMIT
Debug Receiverstimusus/Shuttle2229—~1.9kAutomated safety check: PassApache-2.0
Debug Bridgegetknit/knit132—~2.2kAutomated safety check: PassGPL-3.0

Similar skills

  • Phone Harness

    ShawnPana/phone-harness

    Control the user's phone — an iPhone through the Mac's iPhone Mirroring window, an Android over adb, or a rented cloud Android: open apps, tap, type, swipe, read the screen.

    3.2k GitHub stars~4.3k tokensUpdated today
    MobileAuto-check passed
  • Mobile QA

    tloncorp/tlon-apps

    Run a mobile QA checklist on a physical Android device over adb for tlon-apps, then triage what fails into fixes.

    107 GitHub stars~2.4k tokensUpdated today
    MobileAuto-check passed
  • Android

    yang1ming/android-harness

    Direct Android device control through ADB. An agent skill from yang1ming/android-harness.

    176 GitHub stars~259 tokensUpdated 2 mo ago
    MobileAuto-check passed
  • Debug Receivers

    timusus/Shuttle2

    Drive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport…

    229 GitHub stars~1.9k tokensUpdated today
    MobileAuto-check passed
  • Debug Bridge

    getknit/knit

    Drive and verify Knit on a device or emulator through the headless debug bridge (am broadcast to app.getknit.knit.debug.<ACTION, replies as JSON) — send a message on one phone and confirm it landed…

    132 GitHub stars~2.2k tokensUpdated 4 days ago
    MobileAuto-check passed
  • Android Emulator Skill

    Moustachauve/WLED-Android

    Production-ready scripts for Android app testing, building, and automation.

    169 GitHub starsUsed in 1 repo~911 tokens
    MobileAuto-check passed

More from skydoves/android-testing-skills

All 50 skills in this repo
  • Asserting Bounds And Dimensions

    skydoves/android-testing-skills

    A skill your agent uses to verify Compose layout measurements from a UI test using assertWidthIsEqualTo, assertHeightIsEqualTo, assertWidthIsAtLeast, assertHeightIsAtLeast…

    333 GitHub stars~3.3k tokensUpdated 4 mo ago
    Auto-check passed
  • Asserting Node State And Text

    skydoves/android-testing-skills

    A skill your agent uses to verify a Compose semantics node's properties from a UI test using assertExists, assertDoesNotExist, assertIsDisplayed, assertIsNotDisplayed, assertIsDeactivated…

    333 GitHub stars~3.6k tokensUpdated 4 mo ago
    Auto-check passed
  • Capturing Preview Screenshots In CI

    skydoves/android-testing-skills

    A skill your agent uses to render every Jetpack Compose @Preview as a screenshot on a real Android device or emulator and publish a browsable HTML catalog from CI.

    333 GitHub stars~4.5k tokensUpdated 4 mo ago
    Auto-check: notes
  • Capturing Screenshots And Screenrecord

    skydoves/android-testing-skills

    A skill your agent uses to capture visual artefacts from a device for test failures, golden image generation, QA repro, and demo videos.

    333 GitHub stars~3.7k tokensUpdated 4 mo ago
    Auto-check passed
  • Choosing Test Rule Vs Runtest

    skydoves/android-testing-skills

    A skill your agent uses to pick the correct Compose UI test entry point.

    333 GitHub stars~4.5k tokensUpdated 4 mo ago
    Auto-check passed
  • Choosing What To Test

    skydoves/android-testing-skills

    A skill your agent uses to pick which behaviors to cover in an Android test suite using Google's five-category state vocabulary plus the explicit "what NOT to test" list from…

    333 GitHub stars~4.6k tokensUpdated 4 mo ago
    Auto-check passed

Works with

Categories

Questions about Writing Espresso Tests

What does Writing Espresso Tests do?

A skill your agent uses to write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...)), onData(...) for AdapterView, RecyclerViewActions…. Writing Espresso Tests is an agent skill from skydoves/android-testing-skills.intended/intending, and IdlingResource registration.

When should I use Writing Espresso Tests?

Writing Espresso Tests fits situations like: write Espresso 3.7.0 tests against Android Views — onView(matcher).perform(action).check(matches(...)); onData(...) for AdapterView; recyclerViewActions; intents.intended/intending.

How do I install Writing Espresso Tests in Claude Code?

Run `npx skills add skydoves/android-testing-skills --skill writing-espresso-tests -a claude-code`. Or copy the skill folder (instrumentation/espresso/writing-espresso-tests in skydoves/android-testing-skills) into .claude/skills/writing-espresso-tests in your project. Claude Code loads it when a task matches its description.

How do I install Writing Espresso Tests in Codex?

Run `npx skills add skydoves/android-testing-skills --skill writing-espresso-tests -a codex`. Or copy the skill folder (instrumentation/espresso/writing-espresso-tests in skydoves/android-testing-skills) into .agents/skills/writing-espresso-tests in your project. Codex loads it when a task matches its description.

Can I use Writing Espresso Tests in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add skydoves/android-testing-skills --skill writing-espresso-tests -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/writing-espresso-tests, .gemini/skills/writing-espresso-tests, .github/skills/writing-espresso-tests and .opencode/skills/writing-espresso-tests in your project.

What does Writing Espresso Tests need to run?

SKILL.md names no scripts, command-line tools or credentials: Writing Espresso Tests is instructions for the agent only.

Does Writing Espresso Tests access the network?

SKILL.md names 1 domain. As links in the text: developer.android.com. This is read from the text; nothing was executed.

Is Writing Espresso Tests safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Writing Espresso Tests use?

Writing Espresso Tests is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Writing Espresso Tests use?

About 5.4k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Writing Espresso Tests?

Skills that share tags, products or a category with Writing Espresso Tests: Phone Harness (ShawnPana/phone-harness, 3.2k stars), Mobile QA (tloncorp/tlon-apps, 107 stars), Android (yang1ming/android-harness, 176 stars) and Debug Receivers (timusus/Shuttle2, 229 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Writing Espresso Tests?

skydoves (a GitHub user) maintains it in skydoves/android-testing-skills, which has 333 GitHub stars. The repository holds 50 skills in this directory. The repository was last updated on May 25, 2026.

Source: skydoves/android-testing-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.