Download - Advanced espresso #io16 extend seoul
![Page 1: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/1.jpg)
정승욱
Google Developer Expert토스랩 - JANDI Android 개발자
Advanced Espresso
![Page 2: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/2.jpg)
안드로이드 테스트
안드로이드
스튜디오
안드로이드 테스트서포트 라이브러리
AndroidEpsresso
![Page 3: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/3.jpg)
UI 테스트 흐름
![Page 4: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/4.jpg)
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 테스트
![Page 5: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/5.jpg)
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 테스트
![Page 6: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/6.jpg)
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 테스트
![Page 7: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/7.jpg)
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 테스트
![Page 8: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/8.jpg)
Espresso 의 구분
onView(Matcher<View>) // ViewMatcher -> ViewInteraction .perform(ViewAction) // ViewAction
onView(Matcher<View>) .check(ViewAssertion); // ViewAssertion
![Page 9: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/9.jpg)
ViewMatcher
View 에 접근하기 위한 객체
Activity 나 Fragment 를 사용하면?
➡� View 가 Null 이면? ➡� Test에 NPE 처리를?
“ViewMatcher 는 뷰에 접근하는 과정에서의 오동작을 에러가 아닌 테스트 실패로 간주할 수 있도록 도와준다.”
![Page 10: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/10.jpg)
ViewInteraction
UI 테스트의 시작점
접근한 View 정보를 담고 있음
View 의 동작을 제어 : 클릭, 텍스트 입력 등
View 의 정보를 검증 기능 제공 : 화면에 보이는지..
![Page 11: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/11.jpg)
ViewAction
뷰에 클릭 또는 텍스트 입력등 다양한 동작을 제어함
동작이 완료될 때까지 대기하도록 함
![Page 12: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/12.jpg)
ViewInteraction.java
![Page 13: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/13.jpg)
Idle or not?
handler.postDelayed(runnable, 5000);
Main Looper 는 Idle 상태일까요?
![Page 14: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/14.jpg)
LooperIdlingResource.java
![Page 15: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/15.jpg)
QueueInterrogator.java
![Page 16: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/16.jpg)
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;}
![Page 17: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/17.jpg)
Custom IdlingResource 적용
@Beforepublic void registerIntentServiceIdlingResource() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); idlingResource = new IntentServiceIdlingResource( instrumentation.getTargetContext()); Espresso.registerIdlingResources(idlingResource);}
@Afterpublic void unregisterIntentServiceIdlingResource() { Espresso.unregisterIdlingResources(idlingResource);}
![Page 18: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/18.jpg)
구글의 팁
![Page 19: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/19.jpg)
복사 붙여넣기 하지마라
![Page 20: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/20.jpg)
복붙 금지!!!
@Test public void testXXX() { onView(withId(R.id.fab)).xxx;}
@Test public void testYYY() { onView(withId(R.id.fab)).yyy;}
만약 Resource 의 ID 가 바뀐다면?
![Page 21: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/21.jpg)
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()); }}
![Page 22: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/22.jpg)
가능한 제공되는 Matcher 를 사용해라
![Page 23: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/23.jpg)
CheatSheet
![Page 24: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/24.jpg)
CountingIdlingResource 를 사용해라
![Page 25: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/25.jpg)
CountingIdlingResource.java
public class CountingIdlingResource {
public void increment();
public void decrement();
}
![Page 26: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/26.jpg)
단 Timeout 설정을 같이 해주세요.
public class IdlingPolicies {
public static void setMasterPolicyTimeout(long timeout,TimeUnit unit);
public static void setIdlingResourceTimeout(long timeout, TimeUnit unit);
}
기본값- IdlingResource : 5초- MasterPolicy : 26초
![Page 27: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/27.jpg)
뷰의 정보가 아닌 동작에 집중해라.
![Page 28: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/28.jpg)
동작의 결과에 주목하자.
4가 쓰여진 뷰의 x-y 위치 같은 것은 잊어라
4가 씌여진 뷰가 있는지를 검증하라.
![Page 29: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/29.jpg)
Large Test 보단 Small Test 를 많이 써라
![Page 30: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/30.jpg)
LargeTest
![Page 31: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/31.jpg)
Small Test
1 2
2 3
3 4
![Page 32: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/32.jpg)
원하는 화면을 바로 호출해라.
![Page 33: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/33.jpg)
MyActivityTest.java
@Rule
new ActivityTestRule<MyActivity>(MyActivity.class) {
@Override protected Intent getActivityIntent() {
Intent intent = new Intent();
intent.putExtra(...);
return intent;
}
}
![Page 34: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/34.jpg)
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);}
![Page 35: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/35.jpg)
통제된 환경에서만 테스트 해라
![Page 36: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/36.jpg)
외부 앱 실행은 Intent 를 획득하라
![Page 37: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/37.jpg)
Intent 획득
@Testpublic void test() { Intents.init(); ActivityResult result = createImageCaptureResult(); intending(hasAction(IMAGE_CAPTURE)).responseWith(result); // test something Intents.release();}
![Page 38: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/38.jpg)
Intent 획득
@Rulepublic IntentTestRule<MyAct> rule = new IntentTestRule<>(MyAct.class);
@Testpublic void test() { ActivityResult result = createImageCaptureResult(); intending(hasAction(IMAGE_CAPTURE)).responseWith(result); // test something}
![Page 39: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/39.jpg)
애니메이션을 핸들링 하기
![Page 40: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/40.jpg)
이따금 커스텀 애니메이션은 과도하게 Handler 를 사용하기 때문에 Idle 상태를 유지하기 어렵게 만든다.
![Page 41: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/41.jpg)
UI 테스트에 실패했을 때...
![Page 42: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/42.jpg)
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**** |
![Page 43: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/43.jpg)
Custom FailureHandler
public interface FailureHandler {
public void handle(Throwable error, Matcher<View> viewMatcher);
}
Espresso.setFailureHandler(handler);
![Page 44: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/44.jpg)
느린 기기 Test 시 주의사항
![Page 45: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/45.jpg)
Settings → Accessiblility → Touch and hold delay
Long 으로 전환
Animation 비활성화
![Page 46: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/46.jpg)
Accessibility Test 시 주의사항
![Page 47: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/47.jpg)
AccessibilityValidator.enable()
![Page 48: Advanced espresso #io16 extend seoul](https://reader031.vdocuments.site/reader031/viewer/2022030302/587d33271a28ab2a448b5747/html5/thumbnails/48.jpg)
참고 자료
문서
- 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