Matchers
Matcher는 에스프레소에서 UI의 요소가 의도된 대로 출력되었는지 확인하는데 사용되는 객체입니다.
일반적으로 TextView의 text가 "No country string"
와 동일한지 체크하는 코틀린 코드는 다음과 같이 작성할 수 있습니다.
onView(withId(R.id.tvLocale))
.check(ViewAssertions.matches(ViewMatchers.withText("No country string")))
ViewAssertions.matches(...)
는 인자로 주어진 Matcher에 정의된 기준으로 View의 요소가 의도된대로 설정되었는지 판단할 수 있습니다.
만약 기준에 부합하지 못한다면 Assertion을 발생시킵니다.
간단히 위의 코드가 어떻게 동작하는지 Espresso 코드를 분석해보겠습니다.
ViewAssertions 분석
ViewAssertion.matches(..)
는 MatchesViewAssertion 객체를 리턴합니다.
public static ViewAssertion matches(final Matcher<? super View> viewMatcher) {
return new MatchesViewAssertion(checkNotNull(viewMatcher));
}
onView(...).check()
는 내부적으로 MatchesViewAssertion.check()
를 호출합니다.
이 메소드는 먼저 일치하지 않을 때 보여줄 description을 미리 생성하고 assertThat()
을 호출합니다.
실제로 assertThat()
내부에서 View의 text와 인자로 넘어온 text를 비교하여 assert를 발생시킬지 말지 결정합니다.
assert가 발생한다면 미리 만들어둔 description을 이용하여 에러 로그를 출력합니다.
static class MatchesViewAssertion implements ViewAssertion {
private MatchesViewAssertion(final Matcher<? super View> viewMatcher) {
this.viewMatcher = viewMatcher;
}
@Override
public void check(View view, NoMatchingViewException noViewException) {
....
description.appendText("' doesn't match the selected view.");
assertThat(description.toString(), view, viewMatcher);
}
}
...
}
ViewMatchers 분석
위에서 assertThat()
은 static 메소드인 ViewMatchers.assertThat()
입니다.
코드를 보면 matcher.matches(actual)
가 false이면 assertion이 발생합니다.
이제 Matcher.matches()
의 코드만 확인하면 됩니다. Matcher 내부에 text를 비교하는 구현부가 존재할 것으로 예상이 되네요.
public static <T> void assertThat(String message, T actual, Matcher<T> matcher) {
if (!matcher.matches(actual)) {
Description description = new StringDescription();
description
.appendText(message)
.appendText("\nExpected: ")
.appendDescriptionOf(matcher)
.appendText("\n Got: ");
if (actual instanceof View) {
description.appendValue(HumanReadables.describe((View) actual));
} else {
description.appendValue(actual);
}
description.appendText("\n");
throw new AssertionFailedError(description.toString());
}
}
Matcher 분석
앞에서 인자로 ViewMatchers.withText("No country string")
를 전달하였는데요.
ViewMatchers.withText()
는 WithTextMatcher 객체를 생성하여 리턴합니다.
public static Matcher<View> withText(final Matcher<String> stringMatcher) {
return new WithTextMatcher(checkNotNull(stringMatcher));
}
WithTextMatcher 객체는 BoundedMatcher를 상속하는 객체인데요.
Text를 비교하는 내용은 matchesSafely()
에 구현되어 있고, Matcher.matches()
내부에서 matchesSafely()
를 호출하는 구조입니다.
static final class WithTextMatcher extends BoundedMatcher<View, TextView> {
.....
@Override
public void describeTo(Description description) {
description.appendText("with text: ");
stringMatcher.describeTo(description);
}
@Override
protected boolean matchesSafely(TextView textView) {
String text = textView.getText().toString();
if (stringMatcher.matches(text)) {
return true;
} else if (textView.getTransformationMethod() != null) {
CharSequence transformedText =
textView.getTransformationMethod().getTransformation(text, textView);
if (transformedText != null) {
return stringMatcher.matches(transformedText.toString());
}
}
return false;
}
@Override
public final boolean matches(Object item) {
......
return matchesSafely((S) item);
}
return false;
}
}
정리
ViewAssertion.check()
는 Matcher.check()
의 true/false 결과에 따라 assertion을 발생할지 결정합니다.
View의 어떤 요소를 어떻게 비교할지는 Matcher.check()
의 구현부에 달려있으며, Custom Matcher를 만들어 사용할 수도 있습니다.
참고
Custom Matcher를 직접 구현하는 방법은 Android Espresso, Custom Matcher 구현 방법을 참고해주세요.
Espresso로 테스트 코드를 작성하는 기본적인 방법은 다음 글들을 참고해주세요.
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력