Testing your app is a necessary part of the Cast development process. Your app should comply with the Cast UX Guidelines and Design Checklist to ensure users have a consistent Cast experience.
For Android apps, leverage the UI Automator and Espresso testing frameworks to simulate user interactions on your app and run your UI tests in an automated and repeatable way. To learn more about automated UI tests, see Automate user interface tests .
This guide describes how to add automated UI tests to your Android sender app.
Set up the test environment
Android Studio is recommended for building and running your app and tests.
On the physical device used for testing, under Settings > Developer options, turn off the following system animations:
- Window animation scale
- Transition animation scale
- Animator duration scale
Example Gradle build file
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
'
}
Add the first Cast UI test
By default, Android Studio provides a source code directory at src/androidTest/java/
to place your instrumented and UI tests. For more
information, see Test types and
location
.
To test if a Cast icon is displayed on the app:
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
()));
}
}
Test Cast connection
This example shows how to simulate user actions connecting to a Cast device:
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
);
}
}
The Cast session and connection state can be retrieved by executing a call on the application's main 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
);
}
}