# Android 测试 (JUnit + Espresso) ## 运行测试 ```bash # 单元测试 ./gradlew test # UI 测试 ./gradlew connectedAndroidTest ``` ## 单元测试 (JUnit) ```kotlin 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) ```kotlin @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(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 测试 ```kotlin @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) } } ```