Управляйте своим приложением из Macrobenchmark

В отличие от большинства тестов пользовательского интерфейса Android, тесты Macrobenchmark выполняются отдельно от самого приложения. Это необходимо для включения таких функций, как остановка процесса приложения и компиляция байт-кода DEX в машинный код.

Вы можете управлять состоянием своего приложения с помощью библиотеки UIAutomator или других механизмов, которые могут управлять целевым приложением из процесса тестирования. Вы не можете использовать Espresso или ActivityScenario для Macrobenchmark, поскольку они планируют работать в общем процессе с приложением.

В следующем примере RecyclerView находит RecyclerView по идентификатору ресурса и несколько раз прокручивает вниз:

Котлин

@Test
fun scrollList() {
    benchmarkRule.measureRepeated(
        // ...
        setupBlock = {
            // Before starting to measure, navigate to the UI to be measured
            val intent = Intent("$packageName.RECYCLER_VIEW_ACTIVITY")
            startActivityAndWait(intent)
        }
    ) {
        val recycler = device.findObject(By.res(packageName, "recycler"))
        // Set gesture margin to avoid triggering gesture navigation
        // with input events from automation.
        recycler.setGestureMargin(device.displayWidth / 5)

        // Scroll down several times
        repeat(3) { recycler.fling(Direction.DOWN) }
    }
}

Ява

@Test
public void scrollList() {
    benchmarkRule.measureRepeated(
        // ...
        /* setupBlock */ scope -> {
            // Before measuring, navigate to the UI to be measured.
            val intent = Intent("$packageName.RECYCLER_VIEW_ACTIVITY")
            scope.startActivityAndWait();
            return Unit.INSTANCE;
        },
        /* measureBlock */ scope -> {
            UiDevice device = scope.getDevice();
            UiObject2 recycler = device.findObject(By.res(scope.getPackageName(), "recycler"));

            // Set gesture margin to avoid triggering gesture navigation
            // with input events from automation.
            recycler.setGestureMargin(device.getDisplayWidth() / 5);

            // Fling the recycler several times.
            for (int i = 0; i < 3; i++) {
                recycler.fling(Direction.DOWN);
            }

            return Unit.INSTANCE;
        }
    );
}

Ваш тест не требует прокрутки пользовательского интерфейса. Вместо этого он может, например, запустить анимацию. Также нет необходимости специально использовать UI Automator. Он собирает показатели производительности, пока система просмотра создает кадры, включая кадры, созданные Jetpack Compose .

Иногда вам нужно протестировать части вашего приложения, к которым нет прямого доступа извне. Это может быть, например, доступ к внутренним действиям, отмеченным exported=false , переход к Fragment или удаление некоторой части вашего пользовательского интерфейса. Тесты должны вручную переходить к этим частям приложения, как пользователь.

Для навигации вручную измените код внутри setupBlock{} чтобы он содержал нужный вам эффект, например нажатие кнопки или пролистывание. Ваш measureBlock{} содержит только те манипуляции с пользовательским интерфейсом, которые вы хотите протестировать:

Котлин

@Test
fun nonExportedActivityScrollList() {
    benchmarkRule.measureRepeated(
        // ...
        setupBlock = setupBenchmark()
    ) {
        // ...
    }
}

private fun setupBenchmark(): MacrobenchmarkScope.() -> Unit = {
    // Before starting to measure, navigate to the UI to be measured
    startActivityAndWait()

    // click a button to launch the target activity.
    // While we use button text  here to find the button, you could also use
    // accessibility info or resourceId.
    val selector = By.text("RecyclerView")
    if (!device.wait(Until.hasObject(selector), 5_500)) {
        fail("Could not find resource in time")
    }
    val launchRecyclerActivity = device.findObject(selector)
    launchRecyclerActivity.click()

    // wait until the activity is shown
    device.wait(
        Until.hasObject(By.clazz("$packageName.NonExportedRecyclerActivity")),
        TimeUnit.SECONDS.toMillis(10)
    )
}

Ява

@Test
public void scrollList() {
    benchmarkRule.measureRepeated(
        // ...
        /* setupBlock */ scope -> {
            // Before measuring, navigate to the default activity.
            scope.startActivityAndWait();

            // Click a button to launch the target activity.
            // While you use resourceId here to find the button, you can also
            // use accessibility info or button text content.
            UiObject2 launchRecyclerActivity = scope.getDevice().findObject(
                By.res(packageName, "launchRecyclerActivity")
            )
            launchRecyclerActivity.click();

            // Wait until activity is shown.
            scope.getDevice().wait(
                Until.hasObject(By.clazz("$packageName.NonExportedRecyclerActivity")),
                10000L
            )

            return Unit.INSTANCE;
        },
        /* measureBlock */ scope -> {
            // ...
        }
    );
}
{% дословно %} {% дословно %} {% дословно %} {% дословно %}