Files
ai-proj-helper/skills-dev/dev-test-plugin/skills/dev-test/android-testing.md
John Qiu 712063071c refactor: 通用技能按类别拆分为独立目录
skills/ → skills-dev(9), skills-req(10), skills-ops(4),
skills-integration(8), skills-biz(4), skills-workflow(7)

generate-marketplace.py 改为自动扫描所有 skills-* 目录。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 11:31:58 +10:30

3.0 KiB

Android 测试 (JUnit + Espresso)

运行测试

# 单元测试
./gradlew test

# UI 测试
./gradlew connectedAndroidTest

单元测试 (JUnit)

class TaskViewModelTest {
    @get:Rule
    val instantTaskRule = InstantTaskExecutorRule()

    @get:Rule
    val coroutineRule = MainCoroutineRule()

    private lateinit var viewModel: TaskViewModel
    private lateinit var repository: FakeTaskRepository

    @Before
    fun setup() {
        repository = FakeTaskRepository()
        viewModel = TaskViewModel(repository)
    }

    @Test
    fun `fetchTasks updates state`() = runTest {
        // Arrange
        repository.addTasks(listOf(
            Task(1, "Task 1"),
            Task(2, "Task 2")
        ))

        // Act
        viewModel.fetchTasks()

        // Assert
        val tasks = viewModel.tasks.first()
        assertEquals(2, tasks.size)
    }

    @Test
    fun `createTask adds task`() = runTest {
        // Act
        viewModel.createTask("New Task")

        // Assert
        val tasks = viewModel.tasks.first()
        assertTrue(tasks.any { it.title == "New Task" })
    }
}

UI 测试 (Espresso)

@RunWith(AndroidJUnit4::class)
@LargeTest
class TaskListActivityTest {

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

    @Test
    fun displayTaskList() {
        onView(withId(R.id.taskList))
            .check(matches(isDisplayed()))
    }

    @Test
    fun clickTask_opensDetail() {
        onView(withId(R.id.taskList))
            .perform(RecyclerViewActions.actionOnItemAtPosition<TaskViewHolder>(0, click()))

        onView(withId(R.id.taskDetail))
            .check(matches(isDisplayed()))
    }

    @Test
    fun addTask_showsInList() {
        // Click add button
        onView(withId(R.id.addButton)).perform(click())

        // Enter title
        onView(withId(R.id.titleInput))
            .perform(typeText("New Task"), closeSoftKeyboard())

        // Save
        onView(withId(R.id.saveButton)).perform(click())

        // Verify in list
        onView(withText("New Task"))
            .check(matches(isDisplayed()))
    }
}

Compose UI 测试

@RunWith(AndroidJUnit4::class)
class TaskListScreenTest {

    @get:Rule
    val composeRule = createComposeRule()

    @Test
    fun taskList_displays() {
        val tasks = listOf(
            Task(1, "Task 1"),
            Task(2, "Task 2")
        )

        composeRule.setContent {
            TaskListScreen(tasks = tasks)
        }

        composeRule.onNodeWithText("Task 1").assertExists()
        composeRule.onNodeWithText("Task 2").assertExists()
    }

    @Test
    fun taskClick_callsOnClick() {
        var clickedId: Int? = null
        val tasks = listOf(Task(1, "Task 1"))

        composeRule.setContent {
            TaskListScreen(
                tasks = tasks,
                onTaskClick = { clickedId = it.id }
            )
        }

        composeRule.onNodeWithText("Task 1").performClick()

        assertEquals(1, clickedId)
    }
}