يوفّر Android مجموعة متنوعة من الأدوات وواجهات برمجة التطبيقات التي يمكن أن تساعدك في إنشاء اختبارات لمختلف أحجام الشاشات والنوافذ.
DeviceConfigurationOverride
تتيح لك العناصر القابلة للتجميع DeviceConfigurationOverride
إلغاء
سمات الإعداد لاختبار أحجام شاشات ونوافذ متعددة في تنسيقات
الإنشاء. يناسب العنصر الذي تم إلغاؤه باستخدام ForcedSize
أي تنسيق في المساحة المتاحة،
ما يتيح لك إجراء أي اختبار
لواجهة المستخدم على أي حجم شاشة. على سبيل المثال، يمكنك استخدام شكل هاتف صغير
لإجراء جميع اختبارات واجهة المستخدم، بما في ذلك اختبارات واجهة المستخدم للهواتف الكبيرة والأجهزة القابلة للطي
والأجهزة اللوحية.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
بالإضافة إلى ذلك، يمكنك استخدام هذا المكوّن القابل للتجميع لضبط حجم الخط والمظاهر وغيرها من السمات التي قد تريد اختبارها على أحجام نوافذ مختلفة.
Robolectric
استخدِم Robolectric لتشغيل اختبارات واجهة المستخدم المستندة إلى Compose أو العرض على JVM محليًا، بدون الحاجة إلى أجهزة أو محاكيات. يمكنك ضبط Robolectric لاستخدام أحجام شاشة معيّنة، بالإضافة إلى خصائص مفيدة أخرى.
في المثال التالي من الميزات الجديدة في Android، تم ضبط Robolectric لمحاكاة حجم شاشة يبلغ 1000×1000 dp بدقة 480 نقطة لكل بوصة:
@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 (GMD) في Android Gradle تحديد مواصفات المحاكيات والأجهزة الحقيقية التي يتم فيها تنفيذ اختباراتك المزوّدة بأدوات قياس الأداء. أنشئ مواصفات للأجهزة التي تتضمّن أحجام شاشات مختلفة لتنفيذ استراتيجية اختبارات يجب فيها إجراء اختبارات معيّنة على أحجام شاشات معيّنة. باستخدام أداة GMD مع الدمج المستمر (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" هو خدمة مدفوعة تتضمّن فئة مجانية. تتيح أداة FTL أيضًا إجراء الاختبارات على المحاكيات. تعمل هذه الخدمات على تحسين موثوقية الاختبار الآلي وسرعته لأنّه يمكنها توفير الأجهزة والمحاكيات مسبقًا.
للحصول على معلومات عن استخدام FTL مع GMD، اطّلِع على مقالة توسيع نطاق اختباراتك باستخدام الأجهزة المُدارة من Gradle.
اختبار الفلترة باستخدام أداة تشغيل الاختبارات
يجب ألا تتحقّق استراتيجية الاختبار المثلى من العنصر نفسه مرّتين، لذا لا يلزم تشغيل معظم اختبارات واجهة المستخدم على أجهزة متعددة. يمكنك عادةً فلترة اختبارات واجهة المستخدم من خلال إجراء جميع الاختبارات أو معظمها على شكل هاتف ومجموعة فرعية فقط على الأجهزة ذات أحجام شاشات مختلفة.
يمكنك إضافة تعليقات توضيحية لاختبارات معيّنة لتشغيلها على أجهزة معيّنة فقط، ثم تمرير 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 أو 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 لا يزال في المرحلة التجريبية ويتضمّن ال requirements التالية:
- الإصدار 8.3 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو إصدار أحدث
- الإصدار 33.1.10 من "محاكي Android" أو إصدار أحدث
- جهاز Android افتراضي يعمل بالمستوى 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 Repository في 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)))
)
}
يمكنك أيضًا محاكاة ميزات العرض في اختبارات واجهة المستخدم باستخدام الدالة
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.
مصادر إضافية
المستندات
- إرشادات جودة التطبيقات المخصّصة للشاشات الكبيرة
- اختبار التطبيقات على Android
- اختبار تنسيق ميزة "الإنشاء"
نماذج
- نموذج WindowManager
- عيّنات أجهزة Espresso
- الميزات الجديدة في Android
- استخدام اختبار لقطات الشاشة للتحقّق من أحجام الشاشات المختلفة
الدروس التطبيقية حول الترميز