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>
146 lines
3.0 KiB
Markdown
146 lines
3.0 KiB
Markdown
# 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<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 测试
|
|
|
|
```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)
|
|
}
|
|
}
|
|
```
|