בדיקות אוטומטיות של ממשק המשתמש של שולח ב-Android

בדיקת האפליקציה היא חלק בלתי נפרד מתהליך הפיתוח של Cast. האפליקציה צריכה לעמוד בהנחיות לגבי חוויית המשתמש ב-Cast וברשימת המשימות לעיצוב כדי להבטיח שהמשתמשים יהנו מחוויית העברה עקבית.

באפליקציות ל-Android, אפשר להשתמש במסגרות הבדיקה של UI Automator ו-Espresso כדי לדמות אינטראקציות של משתמשים באפליקציה ולהריץ את הבדיקות של ממשק המשתמש באופן אוטומטי וחוזר. למידע נוסף על בדיקות אוטומטיות של ממשק המשתמש, תוכלו לקרוא את המאמר אוטומציה של בדיקות לממשק המשתמש.

במדריך הזה נסביר איך להוסיף בדיקות אוטומטיות של ממשק המשתמש לאפליקציית השולח ב-Android.

הגדרת סביבת הבדיקה

מומלץ להשתמש ב-Android Studio כדי לפתח ולהריץ את האפליקציה והבדיקות שלכם.

במכשיר הפיזי שמשמש לבדיקה, בקטע הגדרות > אפשרויות למפתחים משביתים את אנימציות המערכת הבאות:

  • קנה מידה לאנימציה של חלון
  • קנה מידה לאנימציית מעבר
  • קנה מידה למשך זמן אנימציה

דוגמה לקובץ build של Gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        applicationId "com.example.package"
        minSdkVersion 21
        targetSdkVersion 28
        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 Studio יש ספרייה של קוד מקור ב-src/androidTest/java/ כדי לאפשר את הבדיקות של הכלים ושל ממשק המשתמש. למידע נוסף, קראו את המאמר סוגי בדיקות ומיקום.

כדי לבדוק אם סמל Cast מוצג באפליקציה:

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()));
    }
}

בדיקת החיבור ל-Cast

הדוגמה הזו מראה איך לדמות פעולות משתמש בהתחברות למכשיר CAST:

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);
    }
}

כדי לאחזר את סשן ההעברה ומצב החיבור, אפשר לבצע קריאה ב-thread הראשי של האפליקציה:

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);
    }
}