Advanced espresso #io16 extend seoul
-
Upload
seongug-jung -
Category
Internet
-
view
1.688 -
download
1
Transcript of Advanced espresso #io16 extend seoul
정승욱
Google Developer Expert토스랩 - JANDI Android 개발자
Advanced Espresso
안드로이드 테스트
안드로이드
스튜디오
안드로이드 테스트서포트 라이브러리
AndroidEpsresso
UI 테스트 흐름
onView(withId(R.id.fab)).perform(click());
onView(withItemText("Jason")).perform(click());
onView(withId(R.id.et_message))
.perform(typeText("test"));
onView(withId(R.id.btn_send)).perform((click());
onView(withId(android.R.id.home).perform(click());
onView(withItemText("Jason"))
.check(matches(isDisplayed()));
코드로 보는 UI 테스트
onView(withId(R.id.fab)).perform(click());
onView(withItemText("Jason")).perform(click());
onView(withId(R.id.et_message))
.perform(typeText("test"));
onView(withId(R.id.btn_send)).perform((click());
onView(withId(android.R.id.home).perform(click());
onView(withItemText("Jason"))
.check(matches(isDisplayed()));
코드로 보는 UI 테스트
onView(withId(R.id.fab)).perform(click());
onView(withItemText("Jason")).perform(click());
onView(withId(R.id.et_message))
.perform(typeText("test"));
onView(withId(R.id.btn_send)).perform((click());
onView(withId(android.R.id.home).perform(click());
onView(withItemText("Jason"))
.check(matches(isDisplayed()));
코드로 보는 UI 테스트
onView(withId(R.id.fab)).perform(click());
onView(withItemText("Jason")).perform(click());
onView(withId(R.id.et_message))
.perform(typeText("test"));
onView(withId(R.id.btn_send)).perform((click());
onView(withId(android.R.id.home).perform(click());
onView(withItemText("Jason"))
.check(matches(isDisplayed()));
코드로 보는 UI 테스트
Espresso 의 구분
onView(Matcher<View>) // ViewMatcher -> ViewInteraction .perform(ViewAction) // ViewAction
onView(Matcher<View>) .check(ViewAssertion); // ViewAssertion
ViewMatcher
View 에 접근하기 위한 객체
Activity 나 Fragment 를 사용하면?
➡� View 가 Null 이면? ➡� Test에 NPE 처리를?
“ViewMatcher 는 뷰에 접근하는 과정에서의 오동작을 에러가 아닌 테스트 실패로 간주할 수 있도록 도와준다.”
ViewInteraction
UI 테스트의 시작점
접근한 View 정보를 담고 있음
View 의 동작을 제어 : 클릭, 텍스트 입력 등
View 의 정보를 검증 기능 제공 : 화면에 보이는지..
ViewAction
뷰에 클릭 또는 텍스트 입력등 다양한 동작을 제어함
동작이 완료될 때까지 대기하도록 함
ViewInteraction.java
Idle or not?
handler.postDelayed(runnable, 5000);
Main Looper 는 Idle 상태일까요?
LooperIdlingResource.java
QueueInterrogator.java
Custom IdlingResource 예시
@Overridepublic boolean isIdleNow() { boolean idle = !isIntentServiceRunning(); if (idle && resourceCallback != null) { resourceCallback.onTransitionToIdle(); } return idle;}
private boolean isIntentServiceRunning() { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo info : manager.getRunningServices(Integer.MAX_VALUE)) { if (RepeatService.class.getName().equals(info.service.getClassName())) { return true; } } return false;}
Custom IdlingResource 적용
@Beforepublic void registerIntentServiceIdlingResource() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); idlingResource = new IntentServiceIdlingResource( instrumentation.getTargetContext()); Espresso.registerIdlingResources(idlingResource);}
@Afterpublic void unregisterIntentServiceIdlingResource() { Espresso.unregisterIdlingResources(idlingResource);}
구글의 팁
복사 붙여넣기 하지마라
복붙 금지!!!
@Test public void testXXX() { onView(withId(R.id.fab)).xxx;}
@Test public void testYYY() { onView(withId(R.id.fab)).yyy;}
만약 Resource 의 ID 가 바뀐다면?
Robot 예제
42 입력
onView(withText("4")).perform(click());onView(withText("2")).perform(click());
Robot.input(42);
public static void input(int x) { String y = String.valueOf(x); for (int i = 0; i < y.lengn(); i++) { onView(withText(String.valueOf(y.charAt(i)))) .perform(click()); }}
가능한 제공되는 Matcher 를 사용해라
CheatSheet
CountingIdlingResource 를 사용해라
CountingIdlingResource.java
public class CountingIdlingResource {
public void increment();
public void decrement();
}
단 Timeout 설정을 같이 해주세요.
public class IdlingPolicies {
public static void setMasterPolicyTimeout(long timeout,TimeUnit unit);
public static void setIdlingResourceTimeout(long timeout, TimeUnit unit);
}
기본값- IdlingResource : 5초- MasterPolicy : 26초
뷰의 정보가 아닌 동작에 집중해라.
동작의 결과에 주목하자.
4가 쓰여진 뷰의 x-y 위치 같은 것은 잊어라
4가 씌여진 뷰가 있는지를 검증하라.
Large Test 보단 Small Test 를 많이 써라
LargeTest
Small Test
1 2
2 3
3 4
원하는 화면을 바로 호출해라.
MyActivityTest.java
@Rule
new ActivityTestRule<MyActivity>(MyActivity.class) {
@Override protected Intent getActivityIntent() {
Intent intent = new Intent();
intent.putExtra(...);
return intent;
}
}
MyActivityTest.java
@Rulepublic ActivityTestRule<MyActivity> rule = new ActivityTestRule<MyActivity>(MyActivity.class, true, false);
@Beforepublic void setUp() { int extra = getExtraInt(); Intent intent = new Intent(); rule.launchActivity(intent);}
통제된 환경에서만 테스트 해라
외부 앱 실행은 Intent 를 획득하라
Intent 획득
@Testpublic void test() { Intents.init(); ActivityResult result = createImageCaptureResult(); intending(hasAction(IMAGE_CAPTURE)).responseWith(result); // test something Intents.release();}
Intent 획득
@Rulepublic IntentTestRule<MyAct> rule = new IntentTestRule<>(MyAct.class);
@Testpublic void test() { ActivityResult result = createImageCaptureResult(); intending(hasAction(IMAGE_CAPTURE)).responseWith(result); // test something}
애니메이션을 핸들링 하기
이따금 커스텀 애니메이션은 과도하게 Handler 를 사용하기 때문에 Idle 상태를 유지하기 어렵게 만든다.
UI 테스트에 실패했을 때...
Espresso ViewHierarchy Log
Test 의 Log Console 을 읽는다.
android.support.test.espresso.AmbiguousViewMatcherException: 'with id: is <2131493330>' matches multiple views in the hierarchy. Problem views are marked with '****MATCHES****' below.
+------------->ImageView{id=2131493330, res-name=item_image, desc=Image, visibility=VISIBLE, width=262, height=262, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0} ****MATCHES****
+------------->ImageView{id=2131493330, res-name=item_image, desc=Image, visibility=VISIBLE, width=262, height=262, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0} ****MATCHES**** |
Custom FailureHandler
public interface FailureHandler {
public void handle(Throwable error, Matcher<View> viewMatcher);
}
Espresso.setFailureHandler(handler);
느린 기기 Test 시 주의사항
Settings → Accessiblility → Touch and hold delay
Long 으로 전환
Animation 비활성화
Accessibility Test 시 주의사항
AccessibilityValidator.enable()
참고 자료
문서
- https://google.github.io/android-testing-support-library/
영상
- https://www.youtube.com/watch?v=isihPOY2vS4
예제 코드- https://github.com/googlesamples/android-testing- https://github.com/googlesamples/android-testing-templates- https://github.com/googlesamples/android-architecture