Thư viện và công cụ để kiểm thử nhiều kích thước màn hình

Android cung cấp nhiều công cụ và API có thể giúp bạn tạo các kiểm thử cho nhiều kích thước màn hình và cửa sổ.

DeviceConfigurationOverride

Thành phần kết hợp DeviceConfigurationOverride cho phép bạn ghi đè các thuộc tính cấu hình để kiểm thử nhiều kích thước màn hình và cửa sổ trong bố cục Compose. Chế độ ghi đè ForcedSize phù hợp với mọi bố cục trong không gian có sẵn, cho phép bạn chạy mọi kiểm thử giao diện người dùng trên mọi kích thước màn hình. Ví dụ: bạn có thể sử dụng kiểu dáng điện thoại nhỏ để chạy tất cả các kiểm thử giao diện người dùng, bao gồm cả kiểm thử giao diện người dùng cho điện thoại lớn, thiết bị có thể gập lại và máy tính bảng.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Hình 1. Sử dụng DeviceConfigurationOverride để điều chỉnh bố cục của máy tính bảng cho phù hợp với thiết bị có hệ số hình dạng nhỏ hơn, như trong \*Now in Android*.

Ngoài ra, bạn có thể dùng thành phần kết hợp này để đặt tỷ lệ phông chữ, giao diện và các thuộc tính khác mà bạn có thể muốn kiểm thử trên nhiều kích thước cửa sổ.

Robolectric

Sử dụng Robolectric để chạy các kiểm thử giao diện người dùng dựa trên Compose hoặc dựa trên khung hiển thị trên JVM cục bộ – không cần thiết bị hoặc trình mô phỏng. Bạn có thể định cấu hình Robolectric để sử dụng các kích thước màn hình cụ thể, cùng với các thuộc tính hữu ích khác.

Trong ví dụ sau đây từ ứng dụng Now in Android, Robolectric được định cấu hình để mô phỏng kích thước màn hình 1000x1000 dp với độ phân giải 480 dpi:

@RunWith(RobolectricTestRunner::class)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
// This allows enough room to render the content under test without clipping or scaling.
@Config(qualifiers = "w1000dp-h1000dp-480dpi")
class NiaAppScreenSizesScreenshotTests { ... }

Bạn cũng có thể đặt các tiêu chí phân loại từ nội dung kiểm thử như trong đoạn mã này trong ví dụ Now in Android:

val (width, height, dpi) = ...

// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")

Xin lưu ý rằng RuntimeEnvironment.setQualifiers() sẽ cập nhật hệ thống và tài nguyên ứng dụng bằng cấu hình mới nhưng không kích hoạt bất kỳ thao tác nào trên các hoạt động đang hoạt động hoặc các thành phần khác.

Bạn có thể đọc thêm trong tài liệu về Cấu hình thiết bị của Robolectric.

Thiết bị do Gradle quản lý

Trình bổ trợ Android cho Gradle thiết bị do Gradle quản lý (GMD) cho phép bạn xác định thông số kỹ thuật của trình mô phỏng và thiết bị thực nơi các kiểm thử đo lường của bạn chạy. Tạo quy cách cho các thiết bị có nhiều kích thước màn hình để triển khai một chiến lược kiểm thử, trong đó một số bài kiểm thử nhất định phải được chạy trên một số kích thước màn hình nhất định. Bằng cách sử dụng GMD với Tích hợp liên tục (CI), bạn có thể đảm bảo rằng các kiểm thử thích hợp sẽ chạy khi cần, cung cấp và khởi chạy trình mô phỏng cũng như đơn giản hoá chế độ thiết lập CI.

android {
    testOptions {
        managedDevices {
            devices {
                // Run with ./gradlew nexusOneApi30DebugAndroidTest.
                nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) {
                    device = "Nexus One"
                    apiLevel = 30
                    // Use the AOSP ATD image for better emulator performance
                    systemImageSource = "aosp-atd"
                }
                // Run with ./gradlew  foldApi34DebugAndroidTest.
                foldApi34(com.android.build.api.dsl.ManagedVirtualDevice) {
                    device = "Pixel Fold"
                    apiLevel = 34
                    systemImageSource = "aosp-atd"
                }
            }
        }
    }
}

Bạn có thể tìm thấy nhiều ví dụ về GMD trong dự án testing-samples.

Phòng thí nghiệm kiểm tra Firebase

Sử dụng Phòng thử nghiệm Firebase (FTL) hoặc một dịch vụ tương tự về trang trại thiết bị để chạy các kiểm thử trên những thiết bị thực cụ thể mà bạn có thể không truy cập được, chẳng hạn như thiết bị có thể gập lại hoặc máy tính bảng có nhiều kích thước. Phòng thử nghiệm Firebase là một dịch vụ có tính phí kèm theo một cấp miễn phí. FTL cũng hỗ trợ chạy các kiểm thử trên trình mô phỏng. Các dịch vụ này cải thiện độ tin cậy và tốc độ của kiểm thử đo lường vì chúng có thể cung cấp thiết bị và trình mô phỏng trước thời hạn.

Để biết thông tin về cách sử dụng FTL với GMD, hãy xem phần Mở rộng quy mô kiểm thử bằng thiết bị do Gradle quản lý.

Kiểm thử việc lọc bằng trình chạy kiểm thử

Một chiến lược kiểm thử tối ưu không nên xác minh cùng một nội dung hai lần, vì vậy, hầu hết các kiểm thử giao diện người dùng của bạn không cần chạy trên nhiều thiết bị. Thông thường, bạn sẽ lọc các kiểm thử giao diện người dùng bằng cách chạy tất cả hoặc hầu hết các kiểm thử đó trên kiểu dáng điện thoại và chỉ một số ít trên các thiết bị có kích thước màn hình khác nhau.

Bạn có thể chú thích một số kiểm thử để chỉ chạy với một số thiết bị nhất định, sau đó truyền một đối số đến AndroidJUnitRunner bằng lệnh chạy các kiểm thử.

Ví dụ: bạn có thể tạo nhiều chú thích:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Và sử dụng chúng trong các kiểm thử khác nhau:

class MyTestClass {

    @Test
    @TestExpandedWidth
    fun myExample_worksOnTablet() {
        ...
    }

    @Test
    @TestCompactWidth
    fun myExample_worksOnPortraitPhone() {
        ...
    }

}

Sau đó, bạn có thể dùng thuộc tính android.testInstrumentationRunnerArguments.annotation khi chạy các kiểm thử để lọc những kiểm thử cụ thể. Ví dụ: nếu bạn đang sử dụng các thiết bị do Gradle quản lý:

$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

Nếu bạn không sử dụng GMD và quản lý trình mô phỏng trên CI, trước tiên, hãy đảm bảo rằng trình mô phỏng hoặc thiết bị chính xác đã sẵn sàng và được kết nối, sau đó truyền tham số đến một trong các lệnh Gradle để chạy các kiểm thử được đo lường:

$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

Xin lưu ý rằng Espresso Device (xem phần tiếp theo) cũng có thể lọc các kiểm thử bằng cách sử dụng các thuộc tính của thiết bị.

Thiết bị Espresso

Sử dụng Espresso Device để thực hiện các thao tác trên trình mô phỏng trong các quy trình kiểm thử bằng cách sử dụng bất kỳ loại quy trình kiểm thử đo lường nào, bao gồm cả quy trình kiểm thử Espresso, Compose hoặc UI Automator. Các thao tác này có thể bao gồm việc đặt kích thước màn hình hoặc chuyển đổi trạng thái/tư thế gập của thiết bị có thể gập lại. Ví dụ: bạn có thể điều khiển một trình mô phỏng thiết bị có thể gập lại và đặt trình mô phỏng đó ở chế độ trên máy tính. Espresso Device cũng chứa các quy tắc và chú thích JUnit để yêu cầu một số tính năng nhất định:

@RunWith(AndroidJUnit4::class)
class OnDeviceTest {

    @get:Rule(order=1) val activityScenarioRule = activityScenarioRule<MainActivity>()

    @get:Rule(order=2) val screenOrientationRule: ScreenOrientationRule =
        ScreenOrientationRule(ScreenOrientation.PORTRAIT)

    @Test
    fun tabletopMode_playerIsDisplayed() {
        // Set the device to tabletop mode.
        onDevice().setTabletopMode()
        onView(withId(R.id.player)).check(matches(isDisplayed()))
    }
}

Xin lưu ý rằng Espresso Device vẫn đang ở giai đoạn alpha và có các yêu cầu sau:

  • Trình bổ trợ Android cho Gradle 8.3 trở lên
  • Trình mô phỏng Android 33.1.10 trở lên
  • Thiết bị ảo Android chạy API cấp 24 trở lên

Lọc thử nghiệm

Espresso Device có thể đọc các thuộc tính của thiết bị đã kết nối để cho phép bạn lọc các kiểm thử bằng cách sử dụng chú thích. Nếu không đáp ứng các yêu cầu được chú thích, thì các kiểm thử sẽ bị bỏ qua.

Chú thích RequiresDeviceMode

Bạn có thể dùng chú thích RequiresDeviceMode nhiều lần để cho biết một kiểm thử sẽ chỉ chạy nếu thiết bị hỗ trợ tất cả các giá trị DeviceMode.

class OnDeviceTest {
    ...
    @Test
    @RequiresDeviceMode(TABLETOP)
    @RequiresDeviceMode(BOOK)
    fun tabletopMode_playerIdDisplayed() {
        // Set the device to tabletop mode.
        onDevice().setTabletopMode()
        onView(withId(R.id.player)).check(matches(isDisplayed()))
    }
}

Chú thích RequiresDisplay

Chú thích RequiresDisplay cho phép bạn chỉ định chiều rộng và chiều cao của màn hình thiết bị bằng cách sử dụng các lớp kích thước. Các lớp này xác định các nhóm kích thước theo các lớp kích thước cửa sổ chính thức.

class OnDeviceTest {
    ...
    @Test
    @RequiresDisplay(EXPANDED, COMPACT)
    fun myScreen_expandedWidthCompactHeight() {
        ...
    }
}

Đổi kích thước màn hình

Sử dụng phương thức setDisplaySize() để đổi kích thước màn hình trong thời gian chạy. Sử dụng phương thức này cùng với lớp DisplaySizeRule. Lớp này đảm bảo rằng mọi thay đổi được thực hiện trong quá trình kiểm thử sẽ được huỷ trước lần kiểm thử tiếp theo.

@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {

    @get:Rule(order = 1) val activityScenarioRule = activityScenarioRule<MainActivity>()

    // Test rule for restoring device to its starting display size when a test case finishes.
    @get:Rule(order = 2) val displaySizeRule: DisplaySizeRule = DisplaySizeRule()

    @Test
    fun resizeWindow_compact() {
        onDevice().setDisplaySize(
            widthSizeClass = WidthSizeClass.COMPACT,
            heightSizeClass = HeightSizeClass.COMPACT
        )
        // Verify visual attributes or state restoration.
    }
}

Khi thay đổi kích thước màn hình bằng setDisplaySize(), bạn sẽ không ảnh hưởng đến mật độ của thiết bị. Do đó, nếu một phương diện không phù hợp với thiết bị mục tiêu, thì quá trình kiểm thử sẽ thất bại với UnsupportedDeviceOperationException. Để ngăn các kiểm thử chạy trong trường hợp này, hãy dùng chú thích RequiresDisplay để lọc chúng:

@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {

    @get:Rule(order = 1) var activityScenarioRule = activityScenarioRule<MainActivity>()

    // Test rule for restoring device to its starting display size when a test case finishes.
    @get:Rule(order = 2) var displaySizeRule: DisplaySizeRule = DisplaySizeRule()

    /**
     * Setting the display size to EXPANDED would fail in small devices, so the [RequiresDisplay]
     * annotation prevents this test from being run on devices outside the EXPANDED buckets.
     */
    @RequiresDisplay(
        widthSizeClass = WidthSizeClassEnum.EXPANDED,
        heightSizeClass = HeightSizeClassEnum.EXPANDED
    )
    @Test
    fun resizeWindow_expanded() {
        onDevice().setDisplaySize(
            widthSizeClass = WidthSizeClass.EXPANDED,
            heightSizeClass = HeightSizeClass.EXPANDED
        )
        // Verify visual attributes or state restoration.
    }
}

StateRestorationTester

Lớp StateRestorationTester được dùng để kiểm thử việc khôi phục trạng thái cho các thành phần kết hợp mà không cần tạo lại các hoạt động. Điều này giúp các kiểm thử nhanh hơn và đáng tin cậy hơn, vì việc tạo lại hoạt động là một quy trình phức tạp với nhiều cơ chế đồng bộ hoá:

@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
    val stateRestorationTester = StateRestorationTester(composeTestRule)

    // Set content through the StateRestorationTester object.
    stateRestorationTester.setContent {
        MyApp()
    }

    // Simulate a config change.
    stateRestorationTester.emulateSavedInstanceStateRestore()
}

Thư viện Kiểm thử cửa sổ

Thư viện Kiểm thử cửa sổ chứa các tiện ích giúp bạn viết các kiểm thử dựa trên hoặc xác minh các tính năng liên quan đến việc quản lý cửa sổ, chẳng hạn như nhúng hoạt động hoặc các tính năng có thể gập lại. Cấu phần phần mềm này có trong Kho lưu trữ Maven của Google.

Ví dụ: bạn có thể dùng hàm FoldingFeature() để tạo một FoldingFeature tuỳ chỉnh mà bạn có thể dùng trong bản xem trước Compose. Trong Java, hãy dùng hàm createFoldingFeature().

Trong bản xem trước Compose, bạn có thể triển khai FoldingFeature theo cách sau:

@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
    MyApplicationTheme {
        ExampleScreen(
            displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
        )
 }

Ngoài ra, bạn có thể mô phỏng các tính năng hiển thị trong các bài kiểm thử giao diện người dùng bằng cách sử dụng hàm TestWindowLayoutInfo(). Ví dụ sau đây mô phỏng FoldingFeature có bản lề dọc HALF_OPENED ở giữa màn hình, sau đó kiểm tra xem bố cục có phải là bố cục dự kiến hay không:

Compose

import androidx.window.layout.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule

@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {

    @get:Rule(order=1)
    val composeTestRule = createAndroidComposeRule<ComponentActivity>()

    @get:Rule(order=2)
    val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()

    @Test
    fun foldedWithHinge_foldableUiDisplayed() {
        composeTestRule.setContent {
            MediaPlayerScreen()
        }

        val hinge = FoldingFeature(
            activity = composeTestRule.activity,
            state = HALF_OPENED,
            orientation = VERTICAL,
            size = 2
        )

        val expected = TestWindowLayoutInfo(listOf(hinge))
        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)

        composeTestRule.waitForIdle()

        // Verify that the folding feature is detected and media controls shown.
        composeTestRule.onNodeWithTag("MEDIA_CONTROLS").assertExists()
    }
}

Số lượt xem

import androidx.window.layout.FoldingFeature.Orientation
import androidx.window.layout.FoldingFeature.State
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule

@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {

    @get:Rule(order=1)
    val activityRule = ActivityScenarioRule(MediaPlayerActivity::class.java)

    @get:Rule(order=2)
    val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()

    @Test
    fun foldedWithHinge_foldableUiDisplayed() {
        activityRule.scenario.onActivity { activity ->
            val feature = FoldingFeature(
                activity = activity,
                state = State.HALF_OPENED,
                orientation = Orientation.VERTICAL)
            val expected = TestWindowLayoutInfo(listOf(feature))
            windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
        }

        // Verify that the folding feature is detected and media controls shown.
        onView(withId(R.id.media_controls)).check(matches(isDisplayed()))
    }
}

Bạn có thể tìm thấy nhiều mẫu hơn trong dự án WindowManager.

Tài nguyên khác

Tài liệu

Mẫu

Lớp học lập trình