إجراء اختبارات تلقائية لواجهة مستخدم مرسل Android

يُعد اختبار تطبيقك جزءًا ضروريًا من عملية تطوير Cast. يجب أن يتوافق تطبيقك مع إرشادات تجربة المستخدم في Cast وقائمة التحقّق من التصميم لضمان حصول المستخدمين على تجربة متسقة في Cast.

بالنسبة إلى تطبيقات Android، يمكنك الاستفادة من إطارات عمل الاختبار UI Automator وEspresso لمحاكاة تفاعلات المستخدمين مع تطبيقك وإجراء اختبارات واجهة المستخدم بطريقة مبرمَجة وقابلة للتكرار. لمزيد من المعلومات عن اختبارات واجهة المستخدم المبرمَجة، يُرجى الاطّلاع على أتمتة اختبارات واجهة المستخدم.

يوضّح هذا الدليل كيفية إضافة اختبارات واجهة المستخدم الآلية إلى تطبيق المرسِل على Android.

إعداد بيئة الاختبار

ننصح باستخدام استوديو Android لإنشاء تطبيقك وتشغيله وإجراء الاختبارات.

على الجهاز الفعلي المستخدَم للاختبار، ضمن الإعدادات > خيارات المطوّرين، أوقِف رسوم النظام المتحركة التالية:

  • حجم الرسوم المتحركة للنافذة
  • حجم الرسوم المتحركة للنقل
  • طول مدة الرسوم المتحركة

مثال على ملف تصميم Gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 34

    defaultConfig {
        applicationId "com.example.package"
        minSdkVersion 23
        targetSdkVersion 34
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    ...

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test:rules:1.1.1'
}

إضافة أول اختبار لواجهة مستخدم Cast

يوفّر "استوديو Android" تلقائيًا دليل الرمز المصدري في src/androidTest/java/ لوضع الاختبارات المزوّدة بأدوات والاختبارات الخاصة بواجهة المستخدم. لمزيد من المعلومات، يُرجى الاطّلاع على أنواع الاختبارات وموقعها الجغرافي.

لاختبار ما إذا كان رمز البث يظهر على التطبيق، اتّبِع الخطوات التالية:

package com.example.package;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import androidx.mediarouter.app.MediaRouteButton;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import androidx.test.rule.ActivityTestRule;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;

@RunWith(AndroidJUnit4ClassRunner.class)
public class MyCastUITest {

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
            new ActivityTestRule<>(MainActivity.class);

    @Test
    public void testCastButtonDisplay() throws InterruptedException {
        // wait for Cast button
        Thread.sleep(2000);

     onView(isAssignableFrom(MediaRouteButton.class)).check(matches(isDisplayed()));
    }
}

اختبار اتصال البث

يوضّح هذا المثال كيفية محاكاة إجراءات المستخدمين عند الاتصال بجهاز بث:

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;

import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

@RunWith(AndroidJUnit4ClassRunner.class)
public class MyCastUITest {

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
            new ActivityTestRule<>(MainActivity.class);

    /**
     * Connecting to Cast device
     *  - Open Cast menu dialog when tapping the Cast icon
     *  - Select target Cast device and connect
     *  - Assert the Cast state is connected
     */
    @Test
    public void testConnectToCastDevice()
             throws InterruptedException, UiObjectNotFoundException {

        // wait for Cast button ready
        Thread.sleep(2000);

        // click on Cast icon and show a dialog
        onView(isAssignableFrom(MediaRouteButton.class))
                .perform(click());
        onView(withId(R.id.action_bar_root))
                .check(matches(isDisplayed()));

        // select target Cast device to connect
        UiDevice mDevice = UiDevice.getInstance(
                InstrumentationRegistry.getInstrumentation());
        mDevice.findObject(new UiSelector().text(TARGET_DEVICE)).click();

        // assert the Cast state is connected
        assertCastStateIsConnected(MAX_TIMEOUT_MS);
    }
}

يمكن استرداد حالة جلسة Cast وحالة الاتصال من خلال تنفيذ طلب في سلسلة التعليمات الرئيسية للتطبيق:

import android.content.Context;
import android.os.SystemClock;

import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.cast.framework.CastSession;
import com.google.android.gms.cast.framework.SessionManager;

import static org.junit.Assert.assertTrue;

@RunWith(AndroidJUnit4ClassRunner.class)
public class MyCastUITest {
    private CastContext mCastContext;
    private CastSession mCastSession;
    private SessionManager mSessionManager;
    private boolean isCastConnected;

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
            new ActivityTestRule<>(MainActivity.class);

    /**
     * Connecting to Cast device
     */
    @Test
    public void testConnectToCastDevice()
             throws InterruptedException, UiObjectNotFoundException {
        ......

        // assert the Cast state is connected
        assertCastStateIsConnected(MAX_TIMEOUT_MS);
    }

    /**
     * Check connection status from Cast session
     */
    private void assertCastStateIsConnected(long timeout)
              throws InterruptedException {

        long startTime = SystemClock.uptimeMillis();
        isCastConnected = false;

        while (!isCastConnected && SystemClock.uptimeMillis() - startTime < timeout) {

            Thread.sleep(500);

            // get cast instance and cast session from the app's main thread
            InstrumentationRegistry.getInstrumentation().runOnMainSync(
                    new Runnable() {
                        @Override
                        public void run() {
                            Context mTargetContext =
                                InstrumentationRegistry.getInstrumentation().getTargetContext();
                            mCastContext =
                                CastContext.getSharedInstance(mTargetContext);
                            mSessionManager = mCastContext.getSessionManager();
                            mCastSession =
                                mSessionManager.getCurrentCastSession();
                            isCastConnected = mCastSession.isConnected();
                        }
                    }
            );
        }

        assertTrue(isCastConnected);
    }
}