يوفّر Android مجموعة متنوعة من الأدوات وواجهات برمجة التطبيقات التي يمكن أن تساعدك في إنشاء اختبارات لأحجام الشاشات والنوافذ المختلفة.
DeviceConfigurationOverride
تتيح لك الدالة المركّبة DeviceConfigurationOverride إلغاء سمات الإعدادات لاختبار أحجام شاشات ونوافذ متعددة في تنسيقات Compose. يتيح لك إلغاء 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 أو طرق العرض على جهاز Java الافتراضي (JVM) محليًا، بدون الحاجة إلى أجهزة أو محاكيات. يمكنك ضبط Robolectric لاستخدام أحجام شاشات معيّنة، من بين خصائص مفيدة أخرى.
في المثال التالي من Now in Android، تم ضبط Robolectric لمحاكاة حجم شاشة يبلغ 1000×1000 بكسل مستقل الكثافة بدقة 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 { ... }
يمكنك أيضًا ضبط المحدّدات من نص الاختبار كما هو موضّح في هذا المقتطف من مثال Now in 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
استخدِم مختبر Firebase الافتراضي أو خدمة مشابهة لمجموعة الأجهزة لإجراء الاختبارات على أجهزة حقيقية معيّنة قد لا تتمكّن من الوصول إليها، مثل الأجهزة القابلة للطي أو الأجهزة اللوحية ذات الأحجام المختلفة. مختبر Firebase الافتراضي هو خدمة مدفوعة تتضمّن فئة مجانية. تتيح Firebase Test Lab أيضًا إجراء الاختبارات على المحاكيات. تعمل هذه الخدمات على تحسين موثوقية وسرعة اختبارات لقياس حالة التطبيق لأنّها توفّر الأجهزة والمحاكيات مسبقًا.
للحصول على معلومات حول استخدام Firebase Test Lab مع الأجهزة المُدارة من Gradle، يُرجى الاطّلاع على توسيع نطاق الاختبارات باستخدام الأجهزة المُدارة من 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 Device (راجِع القسم التالي) يمكنه أيضًا فلترة الاختبارات باستخدام خصائص الجهاز.
جهاز 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 لا يزال في مرحلة الإصدار الأولي، ويتطلب ما يلي:
- الإصدار 8.3 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو إصدار أحدث
- الإصدار 33.1.10 من Android Emulator أو إصدار أحدث
- جهاز 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
يتيح لك التعليق التوضيحي 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 Testing
تحتوي مكتبة Window Testing على أدوات مساعدة لمساعدتك في كتابة اختبارات تعتمد على الميزات المتعلقة بإدارة النوافذ أو التحقّق منها، مثل تضمين الأنشطة أو الميزات القابلة للطي. تتوفّر هذه الأداة من خلال مستودع Maven من Google.
على سبيل المثال، يمكنك استخدام الدالة FoldingFeature() لإنشاء FoldingFeature مخصّص يمكنك استخدامه في معاينات Compose. في 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)))
)
}
يمكنك أيضًا محاكاة ميزات العرض في اختبارات واجهة المستخدم باستخدام الدالة
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 Device
- Now In Android
- يستخدم اختبار لقطات الشاشة للتحقّق من أحجام الشاشات المختلفة
اختبارات الرموز