# Android Test Structure

> Create androidTest directory structure with base classes and utilities

- Skill: `diegosouzapw/android-test-structure` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/android-test-structure`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/android-test-structure/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/android-test-structure

---


# 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

