본 포스팅은 生まれ変わったUI Automatorを使いこなす / How to Use Brand New UI Automator Perfectly 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
실제 슬라이드의 일본어
부분을 번역했다는 점 양해바랍니다.
다시 태어난 UI Automator를 자유자재로 다루기
2016.2.19
@sumio_tym (TOYAMA Sumio)
자기소개
uiautomator/Appium 이야기를 적었습니다 http://www.atmarkit.co.jp/ait/kw/smapho_testtopl.html
발표내용
이야기 흐름
1. 개요
UI Automator2의 개요
BlackBox 테스트
용복수 어플에 걸친
테스트(조작)가 가능 (소스나 apk가 지금 없어도 테스트 가능)구 uiautomator에서 Repogitory 위치가 이동되었기때문에 주의
구 uiautomator와의 차이
구 uiautomator | UI Automator2 | UI Automator2 | |
---|---|---|---|
대응 API 버전 | 16 이상 | 18, 19 | 21 이상 |
IDE / Build Tool | Eclipse/ant | Android Studio / Gradle | Android Studio / Gradle |
실행방법 | uiautomator 커맨드 | Instrumentation Test | Instrumentation Test |
텍스트 입력 | ASCII 뿐 | ASCII 뿐 | Unicode |
퍼포먼스 | 조금 느리다 | 빠르다 | 빠르다 |
구 API 사용 | ○ | △ | ○ |
신 API 사용 | × | △ | ○ |
자세한것은 https://goo.gl/mTP8ig
기본적인 사용방법 - 준비
준비: File 배치와 build.gradle
androidTest
/dependencies {
// for Instrumentation Test
androidTestCompile 'com.android.suuport.test:runner:0.4.1'
androidTestCompile 'com.android.suuport.test:rules:0.4.1'
// for UI Automator2
androidTestCompile \
'com.android.suuport.test.uiautomator:uiautomator-v18:2.1.2'
}
준비: 초기화
@RunWith(AndroidJUnit4.class)
public class MyUiautomatorTest {
@Rule
public ActivityTestRule<...> activityTestRule = ...;
private UiDevice uiDeivce; // UiDevice를 초기화해둔다
@Before
public void setUp() throws Exception {
uiDevice = UiDevice
.getInstance(InstrumentationRegistry.getInstrumentation());
}
...
}
준비: 정리
2. 기본적인 사용방법 - UI Component의 검색과 조작
UI Component의 검색과 조작: 기본형
uiDevice.findObject(<검색조건>).<조작>;
파라매터 · 리턴형에 주목!
UI Component의 검색과 조작: 검색(1/2)
new UiSelector().text("OK").className(Button.class)
By.text("OK").clazz(Button.class)
UI Component의 검색과 조작: 검색(2/2)
메소드 | 개요 |
---|---|
className(Class) | View의 클래스 (EditText나 Button 등) 를 조건으로 지정한다 |
text(String) | View의 표시 문자열을 조건으로 지정한다 |
description(String) | View의 contentDescription 속성값을 조건으로 지정한다 |
resourceId(String) | View의 Resource ID (문자열 표현)을 조건으로 지정한다 |
메소드 | 개요 |
---|---|
clazz(Class) | View의 클래스 (EditText나 Button 등) 를 조건으로 지정한다 |
text(String) | View의 표시 문자열을 조건으로 지정한다 |
desc(String) | View의 contentDescription 속성값을 조건으로 지정한다 |
res(String) | View의 Resource ID (문자열 표현)을 조건으로 지정한다 |
UI Component의 검색과 조작: 조작
(UiObject/UiObject2 모두 동일한 함수명)
메소드 | 개요 |
---|---|
click() | Click 한다 |
setText(String) | 지정된 문자열을 입력한다 |
getText() | 지정되어있는 문자열을 취득한다 |
isFocused() | 포커스가 On 되어있는지를 취득한다 |
UI Component의 검색과 조작: 사용 예
「”OK” 버튼」을 클릭하는 예
// UiSelector/UiObject를 사용하는 경우
UiObject okButton = uiDevice.findObject(new UiSelector()
.text("OK")
.className(Button.class));
okButton.click();
// BySelector/UiObject2를 사용하는 경우
UiObject2 okButton2 = uiDevice.findObject(By.
.text("OK")
.clazz(Button.class));
okButton2.click();
UI Component의 검색과 조작: 검색 타이밍
UiSelector/UiObject를 사용하는 경우
불가능
BySelector/UiObject2를 사용하는 경우
UI Component의 검색과 조작: 검색 범위 지정
어느 UI SubTree의 아래만 검색한다
검색 시작점
UiSelector criteria = <검색 시작점>.childSelector(<검색조건>);
UiObject target = uiDevice.findObject(criteria);
UiObject2 root = uiDevice.findObject(<검색 시작점>);
UiObject2 target = root.findObject(<검색조건>);
UI Component의 검색과 조작: 검색 범위 지정
어느 UI SubTree의 아래만 검색한다
「layout2」의 아래에 있는 「OK」 버튼을 누른다
UiSelector criteria = new UiSelector()
.resourceIdMatches(".*:id/layout2")
.childSelector(new UiSelector().text("OK"));
uiDevice.findObject(criteria).click();
UiObject2 root = uiDevice.findObject(By.res(Pattern.compile(".*:id/layout2")));
root.findObject(By.text("OK")).click();
UI Component의 검색과 조작: 검색 범위 지정
주의점 : 「탐색 깊이
」를 지정하고 싶을 경우
「layout1」의 직접의 자식 (탐색 깊이 1 한정)의 「OK」 버튼을 누르고 싶다
UiObject2 root = uiDevice.findObject(By.res(Pattern.compile("*.id:/layout1")));
root.findObject(By.text("OK").depth(1)).click();
UI Component의 검색과 조작: 스크롤
스크롤하면 나타나는 화면 밖의 Component 조작
// findObject() 미사용
UiScrollable list = new UiScrollable(new UiSelector().className(ListView.class));
devOptionsList.setAsVerticalList();
// 「"Show touches"를 가지는 자식 LinearLayout」를 찾는다
UiObject showTouches = list.getChildByText(new UiSelector().className(LinearLayout.class), "Show touches");
showTouches.click();
[Developer options] → [Show touches]를 탭하고 싶다
UI Component의 검색과 조작: 스크롤
스크롤하면 나타나는 화면 밖의 Component 조작
UI Component의 검색과 조작: 정리
2종류
존재
검색 타이밍
이 미묘하게 다르다
세심한 부분을 개선
구 API뿐
기본적인 사용 방법 - Synchronous
Synchronous: 개요
Synchronous가 필요
Synchronous: UiSelector/UiObject
UiObject의 「조작 + 동기」
메소드를 호출
메소드 (매개변수는 Timeout 값) | 개요 |
---|---|
clickAndWaitForNewWindow(long) | 클릭하고 나서 새로운 Window (Dialog 등)가 표시될 때까지 대기 |
waitForExists(long) | View가 표시될 때까지 대기 |
waitUntilGone(long) | View가 사라질 때까지 대기 |
※ Timeout한 경우는 false가 반환된다
// 「OK」 버튼을 누르고, Dialog가 표현될 때까지 대기
UiObject okButton = uiDevice.findObject(new UiSelector()
.text("OK")
.className(Button.class));
// Dialog가 표실될 때까지 2000msec 대기. Timeout시는 테스트 실패
assertThat(okButton.clickAndWaitForNewWindow(2000L), is(true));
// 여기서부터 표시된 Dialog에 대해 조작을 한다
Synchronous: BySelector/UiObject2
UiObject2의 범용적인
메소드를 호출
메소드 (매개변수는 Timeout 값) | 개요 |
---|---|
clickAndWait(EventCondition, long) | 클릭하고 나서 지정된 조건이 만족할 때까지 대기 |
wait(SearchCondition, long) | 지정된 조건이 만족할 때까지 대기 |
wait(UiObject2Condition, long) | 지정된 조건이 만족할 때까지 대기 |
※ Timeout한 경우는 null/0/false가 반환된다
자주 사용하는 「조건」은 Until Class
가 준비되어 있다
Until Class의 static 함수 | 개요 |
---|---|
newWindow() | 새로운 Window(Dialog)가 표시될 때까지 대기 |
gone(BySelector) | 조건에 일치하는 View를 찾을 수 없을 때까지 |
textEquals(String) | 표시 문자열이 매개변수대로 지정될 때까지 |
Synchronous: BySelector/UiObject2
// 「OK」 버튼을 누르고, Dialog가 표시될 때까지 대기
UiObject2 okButton2 = uiDevice.findObject(By.text("OK").clazz(Button.class));
// Dialog가 표시될 때까지 2000msec 대기
boolean result = okButton2.clickAndWait(Until.newWindow(), 2000L);
// Timeout 되었을 때는 테스트를 실패시킨다
assertThat(result, is(true));
//여기서부터 Dialog에 대해 조작을 한다
Synchronous: 언제든지 사용가능한 것
UiDevice Class의 메소드를 호출
메소드 (매개변수는 Timeout 값) | 개요 |
---|---|
wait(SearchCondition, long) | 지정된 조건이 만족할 때까지 대기 |
waitForWindowUpdate(Strimg, long) | 동일 화면 내의 UI가 갱신될 때까지 대기 ※ 첫 번째 매개변수는 어플의 Package 명 |
performActionAndWait(Runnable, EventCondition, long) | 지정된 액션(Runnable)을 실행하고 조건이 만족할 때까지 대기 |
waitForIdle(long) | 이 어플이 IDle 상태가 될 때까지 대기 |
Synchronous: 주의
※ 틀리면 Timeout하므로 주의
)
wait(Until.hasObject(By.pkg(...)), timeout)
)
Synchronous: 정리
3. Tips
UI 감시: UiWatcher
찾을 수 없을 때
에 발동하는 Listener
테스트를 실패시키기 어렵게 하는 것에 유효!
UI 감시: 주의점
몇 번이라도 호출할 수 있다
1번만
호출할 수 있다Timeout 값: 정의
Timeout 값: 대기 타이밍
Home/Back 키
등 누르기 직전 (이 시간내에 Idle 상태가 되지 않으면 그만두고 키를 누름)UiObject2
를 검색 · 조작하기 직전 (이 시간내에 Idle 상태가 되지 않으면 그만두고 검색 · 조작한다)UiObject
를 검색할 때 (이 시간내에 찾지 못하면 그만둔다)UiObject
를 조작할 때 (이 시간내에 조작이 완료하지 않으면 그만둔다)Timeout 값: 대기 타이밍
사용하는 API(Class)에 따라서 사용되는 Timeout 값이 다른 점에 주의!
UiDevice | UiObject | UiObject2 | |
---|---|---|---|
Idle Timeout | ○ | × | ○ |
Selector Timeout | × | ○ | × |
Action Acknowledgment Timeout | × | ○ | × |
WebView 조작
4. UI Automator2의 사용처
사용처: Espresso와의 겸용
하나의 테스트 메소드
에서 Espresso · UI Automator 양쪽의 API를 겸용 가능
사용처: Use Case
사용처: Espresso겸용시의 주의점
변경시
에는 Synchronous를 잊지 않도록 (Espresso ↔ UI Automator
)
// 주소록에 접근하기 위한 버튼을 누른다
onView(withId(R.id.button_contacts)).perform(click());
// Permission 확인 Dialog를 조작
// UI Automator로 조작할 버튼이 표시될 때까지 대기
uiDevice.wait(Until.findObject(
By.res("com.android.packageinstaller", "permission_allow_button")),
TIMEOUT_VALUE).click();
// 재차 주소록 접근 버튼을 누른다
// Espresso로 조작할 버튼이 표시될 때까지 대기
uiDevice.wait(Until.hasObject(
By.res(Pattern.compile(".*:id/button_contacts"))),
TIMEOUT_VALUE));
onView(withId(R.id.button_contacts)).perform(click());
5. 정리
정리
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024