通过 Macrobenchmark 控制应用

与大多数 Android 界面测试不同,Macrobenchmark 测试在独立于应用本身的进程中运行。必须这样做才能实现停止应用进程和从 DEX 字节码编译到机器码等目的。

您可以使用 UIAutomator 库或其他可以在测试进程中控制目标应用的机制来驱动应用的状态。如需向 UI Automator 公开 Compose 元素,请使用 Modifier.testTag

以下示例将 LazyColumntestTag 结合使用:

@Composable
fun ProductListScreen() {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .testTag("my_lazy_column")    ) {
        items(100) { index ->
            ProductItem(index)
        }
    }
}

该测试使用 testTag 查找 LazyColumn 并快速滑动它:

@Test
fun scrollList() {
    benchmarkRule.measureRepeated(
        packageName = "com.example.myapp",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        setupBlock = {
            uiAutomator {
                pressHome()
                startApp("com.example.myapp")
            }
        }
    ) {
        uiAutomator {
            // Find the Composable using its testTag mapped as a viewIdResourceName
            val lazyColumn = onElement { viewIdResourceName == "my_lazy_column" }

            // Fling the Compose list down
            repeat(3) {
                lazyColumn.fling(Direction.DOWN)
            }
        }
    }
}

基准测试并不一定要滚动界面。不过,举例来说,它可以运行动画。此外,它也不需要特地使用 UI Automator。只要有帧生成,基准测试就会收集性能指标。

有时,您可能需要对应用启动时不会立即显示的特定界面进行基准比较,例如 Jetpack 导航图深处的详情界面或结账页面。

由于 Macrobenchmark 在进程外运行,因此您无法直接与 NavController 互动来切换屏幕。相反,基准测试必须模拟用户导航到应用的相应部分。

使用 setupBlock 处理准备步骤,例如点击初始配置流程或菜单按钮。这样,您的 measureBlock 就只会捕获目标屏幕的效果指标。

@Test
fun deepScreenScrollList() {
    benchmarkRule.measureRepeated(
        packageName = "com.example.myapp",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        setupBlock = {
            uiAutomator {
                // 1. Start the app on the home screen
                startApp("com.example.myapp")

                // 2. Navigate to the internal screen by clicking a Compose component
                // (e.g., a card that opens the target list view)
                val settingsButton = onElement { viewIdResourceName == "go_to_list_button" }
                settingsButton.click()

                // 3. Wait until the target screen settles and is fully rendered
                waitForStableInActiveWindow()
            }
        }
    ) {
        uiAutomator {
            // The actual benchmark measurement starts here on the target screen
            val lazyColumn = onElement { viewIdResourceName == "my_lazy_column" }
            lazyColumn.fling(Direction.DOWN)
        }
    }
}

其他资源

如需详细了解测试,请参阅以下资源。

文档

查看内容