Android มีเครื่องมือและ API มากมายที่ช่วยให้คุณสร้างการทดสอบสำหรับหน้าจอและหน้าต่างขนาดต่างๆ ได้
DeviceConfigurationOverride
คอมโพสิเบิล DeviceConfigurationOverride
ช่วยให้คุณลบล้างแอตทริบิวต์การกําหนดค่าเพื่อทดสอบหน้าจอและหน้าต่างหลายขนาดในเลย์เอาต์คอมโพสิเบิลได้ การลบล้าง ForcedSize
จะปรับให้เหมาะกับเลย์เอาต์ใดก็ได้ในพื้นที่ว่าง ซึ่งจะช่วยให้คุณทำการทดสอบ UI บนหน้าจอทุกขนาดได้ เช่น คุณสามารถใช้รูปแบบโทรศัพท์ขนาดเล็กเพื่อทำการทดสอบ UI ทั้งหมด ซึ่งรวมถึงการทดสอบ UI สำหรับโทรศัพท์ขนาดใหญ่ โทรศัพท์แบบพับ และแท็บเล็ต
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
นอกจากนี้ คุณยังใช้คอมโพสิเบิลนี้เพื่อกำหนดขนาดแบบอักษร ธีม และพร็อพเพอร์ตี้อื่นๆ ที่คุณอาจต้องการทดสอบในหน้าต่างขนาดต่างๆ ได้ด้วย
Robolectric
ใช้ Robolectric เพื่อเรียกใช้การทดสอบ UI ของ Compose หรือ UI ตามมุมมองใน JVMในเครื่องโดยไม่ต้องใช้อุปกรณ์หรือโปรแกรมจำลอง คุณสามารถกําหนดค่า Robolectric ให้ใช้ขนาดหน้าจอที่เฉพาะเจาะจง รวมถึงพร็อพเพอร์ตี้อื่นๆ ที่มีประโยชน์
ในตัวอย่างต่อไปนี้จากตอนนี้ใน Android ระบบกําหนดค่า Robolectric ให้จําลองขนาดหน้าจอ 1000x1000 dp ที่มีความละเอียด 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 { ... }
นอกจากนี้ คุณยังตั้งค่าตัวระบุจากเนื้อหาการทดสอบได้ ดังที่ทำในตัวอย่างข้อมูลโค้ดนี้จากตัวอย่างพร้อมใช้งานใน Android แล้ว
val (width, height, dpi) = ...
// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")
โปรดทราบว่า RuntimeEnvironment.setQualifiers()
จะอัปเดตระบบและทรัพยากรแอปพลิเคชันด้วยการกำหนดค่าใหม่ แต่ไม่ทริกเกอร์การดำเนินการใดๆ ในกิจกรรมที่ใช้งานอยู่หรือคอมโพเนนต์อื่นๆ
อ่านเพิ่มเติมได้ที่เอกสารประกอบการกําหนดค่าอุปกรณ์ของ Robolectric
อุปกรณ์ที่มีการจัดการโดย Gradle
ปลั๊กอิน Gradle สำหรับ Android ของ อุปกรณ์ที่มีการจัดการโดย Gradle (GMD) ช่วยให้คุณกำหนดข้อกำหนดจำเพาะของโปรแกรมจำลองและอุปกรณ์จริงที่จะใช้ทดสอบที่มีการวัดประสิทธิภาพ สร้างข้อมูลจำเพาะสำหรับอุปกรณ์ที่มีหน้าจอขนาดต่างกันเพื่อใช้กลยุทธ์การทดสอบที่ต้องมีการทดสอบบางอย่างในหน้าจอบางขนาด การใช้ GMD กับการรวมอย่างต่อเนื่อง (CI) จะช่วยให้มั่นใจได้ว่าการทดสอบที่เหมาะสมจะทำงานเมื่อจำเป็น การจัดสรรและเปิดใช้งานโปรแกรมจำลอง รวมถึงลดความซับซ้อนของการตั้งค่า 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"
}
}
}
}
}
คุณดูตัวอย่าง GMD หลายรายการได้ในโปรเจ็กต์ testing-samples
Firebase Test Lab
ใช้ Firebase Test Lab (FTL) หรือบริการฟาร์มอุปกรณ์ที่คล้ายกันเพื่อทำการทดสอบบนอุปกรณ์จริงบางรุ่นที่คุณอาจไม่มีสิทธิ์เข้าถึง เช่น อุปกรณ์แบบพับได้หรือแท็บเล็ตขนาดต่างๆ Firebase Test Lab เป็นบริการแบบชําระเงินที่มีรุ่นไม่มีค่าใช้จ่าย นอกจากนี้ FTL ยังรองรับการทดสอบในโปรแกรมจำลองด้วย บริการเหล่านี้ช่วยเพิ่มความน่าเชื่อถือและความเร็วในการทดสอบที่มีเครื่องมือวัดผล เนื่องจากสามารถจัดสรรอุปกรณ์และโปรแกรมจำลองล่วงหน้าได้
ดูข้อมูลเกี่ยวกับการใช้ FTL กับ GMD ได้ที่ปรับขนาดการทดสอบด้วยอุปกรณ์ที่จัดการโดย Gradle
ทดสอบการกรองด้วยโปรแกรมรันทดสอบ
กลยุทธ์การทดสอบที่ดีที่สุดไม่ควรยืนยันสิ่งเดียวกันซ้ำ ดังนั้นการทดสอบ UI ส่วนใหญ่จึงไม่จำเป็นต้องทําในอุปกรณ์หลายเครื่อง โดยปกติแล้ว คุณควรกรองการทดสอบ UI โดยเรียกใช้การทดสอบทั้งหมดหรือส่วนใหญ่ในอุปกรณ์รูปแบบโทรศัพท์ และเรียกใช้เฉพาะการทดสอบชุดย่อยในอุปกรณ์ที่มีหน้าจอขนาดต่างกัน
คุณสามารถกำกับเนื้อหาการทดสอบบางอย่างให้ทำงานกับอุปกรณ์บางเครื่องเท่านั้น แล้วส่งอาร์กิวเมนต์ไปยัง AndroidJUnitRunner โดยใช้คำสั่งที่เรียกใช้การทดสอบ
เช่น คุณสามารถสร้างคำอธิบายประกอบต่างๆ ดังนี้
annotation class TestExpandedWidth
annotation class TestCompactWidth
และใช้กับกลุ่มทดสอบต่างๆ ดังนี้
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
จากนั้นคุณจะใช้พร็อพเพอร์ตี้ android.testInstrumentationRunnerArguments.annotation
เมื่อทำการทดสอบเพื่อกรองการทดสอบที่เฉพาะเจาะจงได้ ตัวอย่างเช่น หากคุณใช้อุปกรณ์ที่มีการจัดการโดย Gradle ให้ทำดังนี้
$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
หากคุณไม่ได้ใช้ GMD และจัดการโปรแกรมจำลองใน CI ก่อนอื่นให้ตรวจสอบว่าโปรแกรมจำลองหรืออุปกรณ์ที่ถูกต้องพร้อมใช้งานและเชื่อมต่อแล้ว จากนั้นส่งพารามิเตอร์ไปยังคําสั่ง Gradle รายการใดรายการหนึ่งต่อไปนี้เพื่อเรียกใช้การทดสอบที่มีเครื่องมือวัด
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
โปรดทราบว่าอุปกรณ์ Espresso (ดูส่วนถัดไป) สามารถกรองการทดสอบโดยใช้พร็อพเพอร์ตี้อุปกรณ์ได้ด้วย
อุปกรณ์ Espresso
ใช้ Espresso Device เพื่อดำเนินการกับโปรแกรมจำลองในการทดสอบโดยใช้การทดสอบที่มีเครื่องมือวัดผลประเภทใดก็ได้ ซึ่งรวมถึงการทดสอบ Espresso, Compose หรือ UI Automator ซึ่งอาจรวมถึงการตั้งค่าขนาดหน้าจอ หรือสลับสถานะหรือลักษณะการพับของอุปกรณ์ เช่น คุณสามารถควบคุมโปรแกรมจำลองแบบพับได้และตั้งค่าเป็นโหมดตั้งโต๊ะ Espresso Device ยังมีกฎและคำอธิบายประกอบ JUnit เพื่อกำหนดให้ต้องใช้ฟีเจอร์บางอย่างด้วย
@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()))
}
}
โปรดทราบว่า Espresso Device ยังอยู่ในระยะอัลฟ่าและมีข้อกำหนดต่อไปนี้
- ปลั๊กอิน Android Gradle 8.3 ขึ้นไป
- Android Emulator 33.1.10 ขึ้นไป
- อุปกรณ์เสมือน Android ที่ใช้ API ระดับ 24 ขึ้นไป
กรองการทดสอบ
Espresso Device สามารถอ่านพร็อพเพอร์ตี้ของอุปกรณ์ที่เชื่อมต่อเพื่อให้คุณกรองการทดสอบได้โดยใช้คำอธิบายประกอบ หากไม่เป็นไปตามข้อกำหนดที่มีการกำกับเนื้อหา ระบบจะข้ามการทดสอบ
คำอธิบายประกอบ RequiresDeviceMode
คุณใช้คำอธิบายประกอบ RequiresDeviceMode
ได้หลายครั้งเพื่อระบุการทดสอบที่จะทํางานก็ต่อเมื่ออุปกรณ์รองรับค่า 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()))
}
}
RequiresDisplay annotation
คําอธิบายประกอบ RequiresDisplay
ช่วยให้คุณระบุความกว้างและความสูงของหน้าจออุปกรณ์ได้โดยใช้คลาสขนาด ซึ่งกําหนดที่เก็บข้อมูลมิติข้อมูลตามคลาสขนาดหน้าต่างอย่างเป็นทางการ
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
ปรับขนาดจอแสดงผล
ใช้เมธอด setDisplaySize()
เพื่อปรับขนาดของหน้าจอขณะรันไทม์ ใช้เมธอดนี้ร่วมกับคลาส DisplaySizeRule
ซึ่งจะตรวจสอบว่าการเปลี่ยนแปลงที่เกิดขึ้นระหว่างการทดสอบถูกเลิกทำก่อนการทดสอบครั้งถัดไป
@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.
}
}
เมื่อปรับขนาดจอแสดงผลด้วย setDisplaySize()
จะไม่ส่งผลต่อความหนาแน่นของอุปกรณ์ ดังนั้นหากขนาดไม่พอดีกับอุปกรณ์เป้าหมาย การทดสอบจะล้มเหลวพร้อม UnsupportedDeviceOperationException
หากต้องการป้องกันไม่ให้การทดสอบทำงานในกรณีนี้ ให้ใช้คำอธิบายประกอบ RequiresDisplay
เพื่อกรองการทดสอบออก
@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
คลาส StateRestorationTester
ใช้เพื่อทดสอบการคืนค่าสถานะสำหรับคอมโพเนนต์แบบคอมโพสิเบิลโดยไม่ต้องสร้างกิจกรรมขึ้นมาใหม่ วิธีนี้ช่วยให้การทดสอบเร็วขึ้นและเชื่อถือได้มากขึ้น เนื่องจากการสร้างกิจกรรมใหม่เป็นกระบวนการที่ซับซ้อนที่มีกลไกการซิงค์หลายอย่าง ดังนี้
@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
val stateRestorationTester = StateRestorationTester(composeTestRule)
// Set content through the StateRestorationTester object.
stateRestorationTester.setContent {
MyApp()
}
// Simulate a config change.
stateRestorationTester.emulateSavedInstanceStateRestore()
}
ไลบรารีการทดสอบกรอบเวลา
ไลบรารีการทดสอบหน้าต่างประกอบด้วยยูทิลิตีที่จะช่วยคุณเขียนการทดสอบที่อาศัยหรือยืนยันฟีเจอร์ที่เกี่ยวข้องกับการจัดการหน้าต่าง เช่น การฝังกิจกรรมหรือฟีเจอร์แบบพับได้ อาร์ติแฟกต์มีให้บริการผ่านที่เก็บ Maven ของ Google
เช่น คุณสามารถใช้ฟังก์ชัน FoldingFeature()
เพื่อสร้าง FoldingFeature
ที่กำหนดเอง ซึ่งจะใช้ในตัวอย่างการเขียนได้ ใน Java ให้ใช้ฟังก์ชัน createFoldingFeature()
ในตัวอย่างการเขียนอีเมล คุณอาจใช้ FoldingFeature
ดังนี้
@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
MyApplicationTheme {
ExampleScreen(
displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
)
}
นอกจากนี้ คุณยังจําลองฟีเจอร์การแสดงผลในการทดสอบ UI ได้โดยใช้ฟังก์ชัน TestWindowLayoutInfo()
ตัวอย่างต่อไปนี้จำลอง FoldingFeature
ที่มีHALF_OPENED
บานพับแนวตั้งตรงกลางหน้าจอ จากนั้นตรวจสอบว่าเลย์เอาต์เป็นรูปแบบที่คาดไว้หรือไม่
เขียน
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()
}
}
ยอดดู
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()))
}
}
ดูตัวอย่างเพิ่มเติมได้ในโปรเจ็กต์ WindowManager
แหล่งข้อมูลเพิ่มเติม
เอกสารประกอบ
ตัวอย่าง
- ตัวอย่าง WindowManager
- ตัวอย่างอุปกรณ์ Espresso
- พร้อมให้ใช้งานใน Android แล้ว
- ใช้การทดสอบภาพหน้าจอเพื่อยืนยันหน้าจอขนาดต่างๆ
Codelabs