Testing UI for Multiple Apps
Dependencies and Prerequisites
- Android 4.3 (API level 18) or higher
- Android Testing Support Library
This lesson teaches you to
You should also read
Try it out
이 문서는 Android Testing Support Library 가 제공하는 UI automator test framework를 사용하여 어떻게 UI 테스트를 작성하는가를 다룬다. UI Automator API는 어떤 액티비티가 포커스를 가지고 있는지 상관없이 장치의 보이는 요소들과 상호작용할 수 있도록 해준다. 당신의 테스트는 컴포넌트에 보여지는 텍스트나 그것의 컨텐츠 디스크립터와 같은 편리한 디스크립터를 사용하여 UI 컴포넌트를 찾을 수 있다. UI Automator 테스트는 안드로이드 4.3(API level 18) 이상에서 동작하는 장치에서 실행할 수 있다. UI Automator 테스팅 프레임워크는 장치 기반의 API이고 Android Testing Support Library test runner와 함께 동작한다.
Set Up UI Automator
UI automator를 사용하여 당신의 UI test를 작성하기 전에 Getting Started with Testing 에 나와 있는데로 당신의 테스트 코드 위치와 프로젝트 디펜던시를 설정해야 한다.
당신의 안드로이드 앱 모듈에 있는 build.gradle 파일에 UI Automator library에 대한 의존성 참조를 셋팅해야 한다:
dependencies { ... androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' }
UI Automator 테스팅을 최적화 하기 위해서는 먼저 타겟 앱의 UI 컴포넌트들을 조사하고 그것들에 접근 가능한지 확인해야 한다. 이들 최적화 팁은 다음 두 섹션에서 설명한다.
Inspecting the UI on a device
테스트를 디자인하기 전에, 장치에 보이는 UI 컴포넌트들을 조사한다. 당신의 UI Automator 테스트가 이들 컴포넌트에 접근할 수 있는지 확인하기 위해 이들 컴포넌트들이 보이는 텍스트 라벨과android:contentDescription 혹은 둘다를
가지고 있는지 체크한다.장치의 foreground 상에 보이는 UI 컴포넌트들의 특성을 보고 레이아웃 계층을 조사하기 위한 편리한 비주얼 인터페이스를 제공하는 uiautomatorviewer 툴을 제공한다. 이 정보들은 UI Automator를 사용하여 더 매끄러운 테스트를 작성할 수 있도록 해준다. 예를 들어 당신은 특정한 보이는 속성과 매치하는 UI selector를 작성할 수 있다.
uiautomatorviewer
툴을 띄우기 위해:To launch the
uiautomatorviewer
tool:- 물리 장치에 타겟 앱을 띄운다.
- 개발 머신에 물리장치를 연결한다.
- 터미널 윈도우를 열어서 <android-sdk>/tools/ 디렉토리로 이동한다.
- 다음 명령을 이용하여 툴을 실행한다.:
$ uiautomatorviewe
uiautomatorviewer
인터페이스 상에서 , Device Screenshot 버튼을 클릭한다..uiautomatorviewer
툴에 의해 인식된 UI 컴포넌트들을 보여주는 왼족 패널 상의 snapshot 상에 마우스 커서를 가져다댄다. 속성들이 오른쪽 패널 하단에 리스팅되고 오른쪽 패널 상단에는 레이아웃 계층이 표시된다.- 선택적으로 UI Automator에서 접근할 수 없는 UI 컴포넌트들을 보기 위해서는 Toggle NAF Nodes 버튼을 클릭한다. 이들 컴포넌트를 위해서는 제한된 정보만 사용할 수 있다.
Ensuring your Activity is accessible
The UI Automator test framework performs better on apps that have implemented Android accessibility features. When you use UI elements of typeView
, or a subclass of View
from the SDK or Support Library, you don't need to implement accessibility support, as these classes have already done that for you.Some apps, however, use custom UI elements to provide a richer user experience. Such elements won't provide automatic accessibility support. If your app contains instances of a subclass of
View
that isn't from the SDK or Support Library, make sure that you add accessibility features to these elements by completing the following steps:- Create a concrete class that extends
ExploreByTouchHelper
. - Associate an instance of your new class with a specific custom UI element by calling
setAccessibilityDelegate()
.
Create a UI Automator Test Class
Your UI Automator test class should be written the same way as a JUnit 4 test class. To learn more about creating JUnit 4 test classes and using JUnit 4 assertions and annotations, see Create an Instrumented Unit Test Class.
Add the
@RunWith(AndroidJUnit4.class)
annotation at the beginning of your test class definition. You also need to specify the AndroidJUnitRunner
class provided in the Android Testing Support Library as your default test runner. This step is described in more detail in Run UI Automator Tests on a Device or Emulator. Implement the following programming model in your UI Automator test class:
- Get a
UiDevice
object to access the device you want to test, by calling thegetInstance()
method and passing it anInstrumentation
object as the argument. - Get a
UiObject
object to access a UI component that is displayed on the device (for example, the current view in the foreground), by calling thefindObject()
method. - Simulate a specific user interaction to perform on that UI component, by calling a
UiObject
method; for example, callperformMultiPointerGesture()
to simulate a multi-touch gesture, andsetText()
to edit a text field. You can call on the APIs in steps 2 and 3 repeatedly as necessary to test more complex user interactions that involve multiple UI components or sequences of user actions. - Check that the UI reflects the expected state or behavior, after these user interactions are performed.
Accessing UI Components
TheUiDevice
object is the primary way you access and manipulate the state of the device. In your tests, you can call UiDevice
methods to check for the state of various properties, such as current orientation or display size. Your test can use the UiDevice
object to perform device-level actions, such as forcing the device into a specific rotation, pressing D-pad hardware buttons, and pressing the Home and Menu buttons.It’s good practice to start your test from the Home screen of the device. From the Home screen (or some other starting location you’ve chosen in the device), you can call the methods provided by the UI Automator API to select and interact with specific UI elements.
The following code snippet shows how your test might get an instance of
UiDevice
and simulate a Home button press:import org.junit.Before; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.By; import android.support.test.uiautomator.Until; ... @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 18) public class ChangeTextBehaviorTest { private static final String BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"; private static final int LAUNCH_TIMEOUT = 5000; private static final String STRING_TO_BE_TYPED = "UiAutomator"; private UiDevice mDevice; @Before public void startMainActivityFromHomeScreen() { // Initialize UiDevice instance mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen mDevice.pressHome(); // Wait for launcher final String launcherPackage = mDevice.getLauncherPackageName(); assertThat(launcherPackage, notNullValue()); mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); // Launch the app Context context = InstrumentationRegistry.getContext(); final Intent intent = context.getPackageManager() .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE); // Clear out any previous instances intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // Wait for the app to appear mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT); } }In the example, the
@SdkSuppress(minSdkVersion = 18)
statement helps to ensure that tests will only run on devices with Android 4.3 (API level 18) or higher, as required by the UI Automator framework.Use the
findObject()
method to retrieve a UiObject
which represents a view that matches a given selector criteria. You can reuse the UiObject
instances that you have created in other parts of your app testing, as needed. Note that the UI Automator test framework searches the current display for a match every time your test uses a UiObject
instance to click on a UI element or query a property.The following snippet shows how your test might construct
UiObject
instances that represent a Cancel button and a OK button in an app.UiObject cancelButton = mDevice.findObject(new UiSelector() .text("Cancel")) .className("android.widget.Button")); UiObject okButton = mDevice.findObject(new UiSelector() .text("OK")) .className("android.widget.Button")); // Simulate a user-click on the OK button, if found. if(okButton.exists() && okButton.isEnabled()) { okButton.click(); }
Specifying a selector
If you want to access a specific UI component in an app, use theUiSelector
class. This class represents a query for specific elements in the currently displayed UI. If more than one matching element is found, the first matching element in the layout hierarchy is returned as the target
UiObject
. When constructing a UiSelector
, you can chain together multiple properties to refine your search. If no matching UI element is found, a UiAutomatorObjectNotFoundException
is thrown. You can use the
childSelector()
method to nest multiple UiSelector
instances. For example, the following code example shows how your test might specify a search to find the first ListView
in the currently displayed UI, then search within that ListView
to find a UI element with the text property Apps.UiObject appItem = new UiObject(new UiSelector() .className("android.widget.ListView") .instance(0) .childSelector(new UiSelector() .text("Apps")));As a best practice, when specifying a selector, you should use a Resource ID (if one is assigned to a UI element) instead of a text element or content-descriptor. Not all elements have a text element (for example, icons in a toolbar). Text selectors are brittle and can lead to test failures if there are minor changes to the UI. They may also not scale across different languages; your text selectors may not match translated strings.
It can be useful to specify the object state in your selector criteria. For example, if you want to select a list of all checked elements so that you can uncheck them, call the
checked()
method with the argument set to true
.Performing Actions
Once your test has obtained aUiObject
object, you can call the methods in the UiObject
class to perform user interactions on the UI component represented by that object. You can specify such actions as:click()
: Clicks the center of the visible bounds of the UI element.dragTo()
: Drags this object to arbitrary coordinates.setText()
: Sets the text in an editable field, after clearing the field's content. Conversely, theclearTextField()
method clears the existing text in an editable field.swipeUp()
: Performs the swipe up action on theUiObject
. Similarly, theswipeDown()
,swipeLeft()
, andswipeRight()
methods perform corresponding actions.
Intent
or launch an Activity
without using shell commands, by getting a Context
object through getContext()
.The following snippet shows how your test can use an
Intent
to launch the app under test. This approach is useful when you are only interested in testing the calculator app, and don't care about the launcher.public void setUp() { ... // Launch a simple calculator app Context context = getInstrumentation().getContext(); Intent intent = context.getPackageManager() .getLaunchIntentForPackage(CALC_PACKAGE); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances context.startActivity(intent); mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT); }
Performing actions on collections
Use theUiCollection
class if you want to simulate user interactions on a collection of items (for example, songs in a music album or a list of emails in an Inbox). To create a UiCollection
object, specify a UiSelector
that searches for a UI container or a wrapper of other child UI elements, such as a layout view that contains child UI elements.The following code snippet shows how your test might construct a
UiCollection
to represent a video album that is displayed within a FrameLayout
:UiCollection videos = new UiCollection(new UiSelector() .className("android.widget.FrameLayout")); // Retrieve the number of videos in this collection: int count = videos.getChildCount(new UiSelector() .className("android.widget.LinearLayout")); // Find a specific video and simulate a user-click on it UiObject video = videos.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "Cute Baby Laughing"); video.click(); // Simulate selecting a checkbox that is associated with the video UiObject checkBox = video.getChild(new UiSelector() .className("android.widget.Checkbox")); if(!checkBox.isSelected()) checkbox.click();
Performing actions on scrollable views
Use theUiScrollable
class to simulate vertical or horizontal scrolling across a display. This technique is helpful when a UI element is positioned off-screen and you need to scroll to bring it into view.The following code snippet shows how to simulate scrolling down the Settings menu and clicking on an About tablet option:
UiScrollable settingsItem = new UiScrollable(new UiSelector() .className("android.widget.ListView")); UiObject about = settingsItem.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "About tablet"); about.click();
Verifying Results
TheInstrumentationTestCase
extends TestCase
, so you can use standard JUnit Assert
methods to test that UI components in the app return the expected results. The following snippet shows how your test can locate several buttons in a calculator app, click on them in order, then verify that the correct result is displayed.
private static final String CALC_PACKAGE = "com.myexample.calc"; public void testTwoPlusThreeEqualsFive() { // Enter an equation: 2 + 3 = ? mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("two")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("plus")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("three")).click(); mDevice.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("equals")).click(); // Verify the result = 5 UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result")); assertEquals("5", result.getText()); }
Run UI Automator Tests on a Device or Emulator
You can run UI Automator tests from Android Studio or from the command-line. Make sure to specify
AndroidJUnitRunner
as the default instrumentation runner in your project. To run your UI Automator test, follow the steps for running instrumented tests described in Getting Started with Testing.
댓글 없음:
댓글 쓰기