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 bài kiểm thử cho kích thước màn hình và cửa sổ khác nhau.

Ghi đè cấu hình thiết bị

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 tra nhiều kích thước màn hình và cửa sổ trong Compose của bạn. Cơ chế ghi đè ForcedSize sẽ vừa với mọi bố cục trong không gian có sẵn, cho phép bạn chạy bất kỳ 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 bài kiểm thử giao diện người dùng, bao gồm cả bài 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 bên trong một thiết bị có kiểu dáng nhỏ hơn, như trong \*Now in Android*.

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

Robolectric

Sử dụng Robolectric để chạy Compose hoặc các kiểm thử giao diện người dùng dựa trên khung hiển thị trên JVM cục bộ – không cần thiết bị hay trình mô phỏng. Bạn có thể định cấu hình Robolectric để sử dụng 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 của ứng dụng Now in Android, Robolectric được định cấu hình để mô phỏng kích thước màn hình 1000 x 1000 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 bộ hạn định trong nội dung kiểm thử như đã thực hiện trong đoạn mã này từ ví dụ cho ứng dụng 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 có cấu hình mới nhưng không kích hoạt bất kỳ hành động nào vào 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 mà các chương trình kiểm thử đo lường của bạn sẽ chạy. Tạo thông số kỹ thuật cho thiết bị có các kích thước màn hình khác nhau để triển khai chiến lược kiểm thử trong đó một số thử nghiệm nhất định phải có thể chạy trên một số kích thước màn hình nhất định. Sử dụng GMD với tính năng Tích hợp liên tục (CI), bạn có thể đảm bảo rằng các thử nghiệm thích hợp sẽ chạy khi cần, cấp phép và khởi chạy trình mô phỏng, đồng thời đơn giản hoá quy trình 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 kiểm thử mẫu.

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ụ theo dõi thiết bị tương tự để chạy các thử nghiệm trên các thiết bị thực cụ thể mà bạn có thể không có quyền truy cập vào, 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à dịch vụ có tính phí với một bậc miễn phí. FTL cũng hỗ trợ chạy 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 độ kiểm thử đo lường vì họ có thể cung cấp trước cho các thiết bị và trình mô phỏng.

Để 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ý.

Lọc thử nghiệm bằng trình chạy kiểm thử

Một chiến lược thử nghiệm tối ưu không nên xác minh cùng một điều hai lần, vì vậy hầu hết Các quy trình kiểm thử giao diện người dùng không cần chạy trên nhiều thiết bị. Thông thường, bạn lọc giao diện người dùng bằng cách chạy tất cả hoặc hầu hết các thử nghiệm trên một kiểu dáng điện thoại và chỉ một tập hợp con 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ố chương trình kiểm thử nhất định để chỉ chạy trên một số thiết bị nhất định, sau đó vượt qua một đối số cho AndroidJUnitRunner bằng lệnh chạy 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 nhiều thử nghiệm:

class MyTestClass {

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

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

}

Sau đó, bạn có thể dùng android.testInstrumentationRunnerArguments.annotation khi chạy thử nghiệm để lọc một số thuộc tính cụ thể. Ví dụ: nếu bạn đang ở bằng 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ý các 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ố vào một trong các lệnh Gradle để chạy kiểm thử đo lường:

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

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

Thiết bị Espresso

Sử dụng Thiết bị Espresso để thực hiện thao tác trên trình mô phỏng trong các kiểm thử bằng cách sử dụng bất kỳ loại hình kiểm thử đo lường, bao gồm Espresso, Compose hoặc kiểm thử Automator trên giao diện người dùng. Bạn có thể thực hiện những thao tác như đặt kích thước màn hình hoặc chuyển đổi trạng thái của thiết bị có thể gập lại hoặc tư thế. 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 đó thành chế độ mặt bàn. Thiết bị Espresso cũng chứa các quy tắc và chú giải 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()))
    }
}

Lưu ý rằng Thiết bị Espresso vẫn đang trong giai đoạn alpha và có các đặc điểm sau các yêu cầu:

  • 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

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

Chú giải yêu cầu DeviceMode

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

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 RequestDisplay

Chú giải 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 lớp kích thước, giúp 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 kích thước của màn hình trong thời gian chạy. Sử dụng phương thức này cùng với 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ử đều bị huỷ trước khi thử nghiệm 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 bạn đổi kích thước màn hình bằng setDisplaySize(), mật độ điểm ảnh sẽ không bị ảnh hưởng của thiết bị, vậy nên nếu một kích thước không vừa với thiết bị mục tiêu, phép thử không thành công với UnsupportedDeviceOperationException. Để ngăn việc kiểm thử đang chạy trong trường hợp này, hãy sử dụng chú giải RequiresDisplay để lọc ra:

@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.
    }
}

Trạng thái phục hồi kiểm thử

Lớp StateRestorationTester dùng để kiểm thử quá trình khôi phục trạng thái cho các thành phần có thể kết hợp mà không cần tạo lại hoạt động. Việc này giúp kiểm thử nhanh hơn và đáng tin cậy hơn, do tạo lại hoạt động là một quá 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 chương trình kiểm thử dựa trên bật hoặc xác minh các tính năng liên quan đến quản lý cửa sổ, chẳng hạn như hoạt động nhúng hoặc có thể gập lại. Cấu phần phần mềm này được cung cấp thông qua Kho lưu trữ Maven.

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

Trong tính năng xem trước trong 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 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 bằng HALF_OPENED bản lề dọc ở giữa màn hình, sau đó kiểm tra xem bố cục là bố cục dự kiến:

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êm mẫu trong dự án WindowManager.

Tài nguyên khác

Tài liệu

Mẫu

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