home / skills / hitoshura25 / claude-devtools / android-test-structure

android-test-structure skill

/skills/android-test-structure

This skill scaffolds androidTest directory structure with base test classes and utilities to accelerate Espresso-based Android UI testing.

npx playbooks add skill hitoshura25/claude-devtools --skill android-test-structure

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
5.7 KB
---
name: android-test-structure
description: Create androidTest directory structure with base classes and utilities
category: android
version: 1.0.0
inputs:
  - project_path: Path to Android project
  - package_name: App package name
outputs:
  - app/src/androidTest/
  - BaseTest.kt
  - TestUtils.kt
verify: "test -d app/src/androidTest"
---

# Android Test Structure

Creates androidTest directory structure with base test class and utilities for Espresso testing.

## Prerequisites

- Android project with Gradle
- Espresso dependencies added (run `android-espresso-dependencies` first)
- Package name known

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| project_path | Yes | . | Android project root |
| package_name | Yes | - | App package (e.g., com.example.app) |

## Process

### Step 1: Create Directory Structure

```bash
# Get package path from package name (com.example.app -> com/example/app)
PACKAGE_PATH=$(echo "${PACKAGE_NAME}" | tr '.' '/')

# Create androidTest directories
mkdir -p "app/src/androidTest/kotlin/${PACKAGE_PATH}/base"
mkdir -p "app/src/androidTest/kotlin/${PACKAGE_PATH}/utils"
mkdir -p "app/src/androidTest/kotlin/${PACKAGE_PATH}/screens"
```

### Step 2: Create BaseTest.kt

Create `app/src/androidTest/kotlin/${PACKAGE_PATH}/base/BaseTest.kt`:

```kotlin
package ${PACKAGE_NAME}.base

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.rules.ActivityScenarioRule
import org.junit.After
import org.junit.Before
import org.junit.Rule

/**
 * Base class for all instrumented tests.
 * Provides common setup, teardown, and utilities.
 */
abstract class BaseTest {

    protected val context: Context = ApplicationProvider.getApplicationContext()

    @Before
    open fun setUp() {
        // Common setup for all tests
        // Override in subclasses for specific setup
    }

    @After
    open fun tearDown() {
        // Common cleanup
        // Override in subclasses for specific cleanup
    }

    /**
     * Wait for idle state before proceeding
     */
    protected fun waitForIdle() {
        androidx.test.espresso.Espresso.onIdle()
    }
}
```

### Step 3: Create TestUtils.kt

Create `app/src/androidTest/kotlin/${PACKAGE_PATH}/utils/TestUtils.kt`:

```kotlin
package ${PACKAGE_NAME}.utils

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import org.hamcrest.Matcher

object TestUtils {

    /**
     * Custom action to get RecyclerView item count
     */
    fun getRecyclerViewItemCount(recyclerView: RecyclerView): Int {
        return recyclerView.adapter?.itemCount ?: 0
    }

    /**
     * Wait for a condition with timeout
     */
    fun waitForCondition(
        timeoutMs: Long = 5000,
        condition: () -> Boolean
    ): Boolean {
        val startTime = System.currentTimeMillis()
        while (System.currentTimeMillis() - startTime < timeoutMs) {
            if (condition()) return true
            Thread.sleep(100)
        }
        return false
    }

    /**
     * Custom ViewAction to perform click on specific position
     */
    fun clickOnViewChild(viewId: Int): ViewAction {
        return object : ViewAction {
            override fun getConstraints(): Matcher<View>? {
                return null
            }

            override fun getDescription(): String {
                return "Click on a child view with specified id."
            }

            override fun perform(uiController: UiController, view: View) {
                val v = view.findViewById<View>(viewId)
                v.performClick()
            }
        }
    }
}
```

### Step 4: Create README for Tests

Create `app/src/androidTest/README.md`:

```markdown
# Android E2E Tests

This directory contains end-to-end UI tests using Espresso.

## Structure

- `base/` - Base classes for all tests
- `utils/` - Test utilities and helpers
- `screens/` - Screen-specific test classes

## Running Tests

```bash
# All tests
./gradlew connectedAndroidTest

# Specific test class
./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.app.screens.MainActivityTest
```

## Writing Tests

1. Extend `BaseTest` for common setup
2. Use utilities from `TestUtils`
3. Follow AAA pattern: Arrange, Act, Assert
```

## Verification

**MANDATORY:** Run these commands:

```bash
# Verify directory structure exists
test -d app/src/androidTest && echo "✓ androidTest directory created"

# Verify base classes exist
test -f app/src/androidTest/kotlin/*/base/BaseTest.kt && echo "✓ BaseTest.kt created"

# Verify utils exist
test -f app/src/androidTest/kotlin/*/utils/TestUtils.kt && echo "✓ TestUtils.kt created"
```

**Expected output:**
- ✓ androidTest directory created
- ✓ BaseTest.kt created
- ✓ TestUtils.kt created

## Outputs

| Output | Location | Description |
|--------|----------|-------------|
| Directory structure | app/src/androidTest/ | Test source directory |
| BaseTest class | base/BaseTest.kt | Common test setup |
| Test utilities | utils/TestUtils.kt | Helper functions |
| README | README.md | Test documentation |

## Troubleshooting

### "Package path incorrect"
**Cause:** Package name format wrong
**Fix:** Use format: com.example.app (not path format)

### "Directory already exists"
**Cause:** androidTest directory already created
**Fix:** Merge with existing structure, don't overwrite

## Completion Criteria

- [ ] `app/src/androidTest/` directory exists
- [ ] `base/BaseTest.kt` exists
- [ ] `utils/TestUtils.kt` exists
- [ ] `screens/` directory exists
- [ ] README.md created

Overview

This skill creates a ready-to-use androidTest directory structure with a base test class and common utilities tailored for Espresso instrumented tests. It scaffolds base/, utils/, and screens/ packages, adds BaseTest.kt and TestUtils.kt, and includes a test README to document usage and running instructions.

How this skill works

The script derives the Kotlin package path from the provided package name and creates the corresponding directories under app/src/androidTest/kotlin/. It writes a BaseTest.kt with common setup/teardown hooks and a TestUtils.kt that contains helper functions like RecyclerView item count, a wait-for-condition helper, and a custom ViewAction for child clicks. It also creates an androidTest README and includes verification commands to confirm files and directories exist.

When to use it

  • Initializing instrumented UI tests in a new or existing Android Gradle project
  • Standardizing test base classes and utilities across a team
  • When you need a quick Espresso-ready test scaffold tied to your app package
  • Before adding screen-specific Espresso tests to ensure consistent setup
  • When you want documented instructions for running connectedAndroidTest

Best practices

  • Provide the exact app package name in dot format (e.g., com.example.app)
  • Run Espresso dependency setup first so tests compile and run
  • Do not blindly overwrite existing androidTest code; merge manually if needed
  • Keep BaseTest focused on shared setup; add scenario-specific setup in subclasses
  • Use TestUtils.waitForCondition for flaky async waits instead of Thread.sleep in tests

Example use cases

  • Scaffold androidTest for a newly generated app so developers can add UI tests quickly
  • Add consistent setup and utilities to multiple apps in a mono-repo by running the script per project
  • Enable CI to run connectedAndroidTest by providing a standard test layout and README
  • Onboard QA engineers by giving them a documented starting point for writing Espresso tests
  • Create screen-specific tests under screens/ that extend BaseTest and reuse TestUtils

FAQ

What package_name format is required?

Use the standard Java package format with dots, for example com.example.app; do not pass a file path.

Will this overwrite existing test files?

The process creates directories and files; if files already exist, merge changes manually to avoid overwriting custom code.