Android มีเครื่องมือและ API มากมายที่ช่วยให้คุณสร้างการทดสอบสำหรับหน้าจอและหน้าต่างขนาดต่างๆ ได้
ลบล้างการกำหนดค่าอุปกรณ์
คอมโพสิเบิล 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.
}
นอกจากนี้คุณยังใช้ Composable นี้เพื่อกำหนดขนาดแบบอักษร ธีม และพร็อพเพอร์ตี้อื่นๆ ที่อาจต้องการทดสอบกับหน้าต่างขนาดต่างๆ ได้ด้วย
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 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()
}
ไลบรารีการทดสอบกรอบเวลา
ไลบรารีการทดสอบ Window มียูทิลิตีที่ช่วยให้คุณเขียนการทดสอบที่อาศัยหรือยืนยันฟีเจอร์ที่เกี่ยวข้องกับการจัดการหน้าต่าง เช่น การฝังกิจกรรมหรือฟีเจอร์แบบพับได้ อาร์ติแฟกต์พร้อมให้ใช้งานผ่านที่เก็บ Maven ของ Google
เช่น คุณสามารถใช้ฟังก์ชัน FoldingFeature()
เพื่อสร้าง FoldingFeature
ที่กำหนดเอง ซึ่งจะใช้ในตัวอย่างการเขียนได้ ใน Java ให้ใช้ฟังก์ชัน createFoldingFeature()
ในตัวอย่าง Compose คุณอาจใช้ 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