Android Sender UI 테스트 자동화

앱 테스트는 Cast 개발 프로세스에서 필수적인 부분입니다. 앱은 Cast UX 가이드라인디자인 체크리스트를 준수하여 사용자에게 일관된 Cast 환경을 제공해야 합니다.

Android 앱의 경우 UI AutomatorEspresso 테스트 프레임워크를 활용하여 앱에서 사용자 상호작용을 시뮬레이션하고 자동화되고 반복 가능한 방식으로 UI 테스트를 실행합니다. 자동화된 UI 테스트에 관한 자세한 내용은 사용자 인터페이스 테스트 자동화를 참고하세요.

이 가이드에서는 Android 발신기 앱에 자동화된 UI 테스트를 추가하는 방법을 설명합니다.

테스트 환경 설정

앱과 테스트를 빌드하고 실행하려면 Android 스튜디오를 사용하는 것이 좋습니다.

테스트에 사용되는 실제 기기의 설정 > 개발자 옵션에서 다음 시스템 애니메이션을 사용 중지합니다.

  • 창 애니메이션 배율
  • 전환 애니메이션 배율
  • 애니메이터 길이 배율

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 UI 테스트 추가

기본적으로 Android 스튜디오는 계측 테스트와 UI 테스트를 배치할 수 있도록 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()));
    }
}

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

애플리케이션의 기본 스레드에서 호출을 실행하여 전송 세션 및 연결 상태를 가져올 수 있습니다.

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