کنترل برنامه از طریق Macrobenchmark (Views)

مفاهیم و پیاده‌سازی Jetpack Compose

برخلاف اکثر تست‌های رابط کاربری اندروید، تست‌های Macrobenchmark در یک فرآیند جداگانه از خود برنامه اجرا می‌شوند. این امر برای فعال کردن مواردی مانند توقف فرآیند برنامه و کامپایل از بایت‌کد DEX به کد ماشین ضروری است.

شما می‌توانید وضعیت برنامه خود را با استفاده از کتابخانه UIAutomator یا سایر مکانیزم‌هایی که می‌توانند برنامه هدف را از فرآیند تست کنترل کنند، هدایت کنید. شما نمی‌توانید از Espresso یا ActivityScenario برای Macrobenchmark استفاده کنید زیرا آنها انتظار دارند در یک فرآیند مشترک با برنامه اجرا شوند.

مثال زیر یک RecyclerView را با استفاده از شناسه منبع آن پیدا می‌کند و چندین بار به پایین اسکرول می‌کند:

کاتلین

@Test
fun scrollList() {
    benchmarkRule.measureRepeated(
        // ...
        setupBlock = {
            uiAutomator {
                // Before starting to measure, navigate to the UI to be measured
                startIntent(Intent("$packageName.RECYCLER_VIEW_ACTIVITY"))
            }
        }
    ) {
        uiAutomator {
            val recycler = onElement { className == "androidx.recyclerview.widget.RecyclerView" }
            // 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 ندارد. این ابزار تا زمانی که فریم‌ها تولید می‌شوند، معیارهای عملکرد را جمع‌آوری می‌کند.

گاهی اوقات می‌خواهید بخش‌هایی از برنامه خود را که مستقیماً از خارج قابل دسترسی نیستند، بنچمارک کنید. این می‌تواند مثلاً دسترسی به فعالیت‌های داخلی باشد که با exported=false مشخص شده‌اند، یا رفتن به یک Fragment ، یا کشیدن قسمتی از رابط کاربری به سمت بیرون. بنچمارک‌ها باید مانند یک کاربر به صورت دستی به این بخش‌های برنامه بروند.

برای پیمایش دستی، کد داخل setupBlock{} را تغییر دهید تا شامل جلوه‌ای باشد که می‌خواهید، مانند ضربه زدن روی دکمه یا کشیدن انگشت. measureBlock{} شما فقط شامل دستکاری رابط کاربری است که می‌خواهید واقعاً آن را محک بزنید:

کاتلین

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

private fun setupBenchmark(): MacrobenchmarkScope.() -> Unit = {
    uiAutomator {
        // Before starting to measure, navigate to the UI to be measured
        startApp(TARGET_PACKAGE)
        // click a button to launch the target activity.
        onElement { textAsString() == "RecyclerView" }.click()
        // wait until the activity is shown
        waitForStableInActiveWindow()
    }
}

جاوا

@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 -> {
            // ...
        }
    );
}
{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}