Espresso 기본사항

이 문서에서는 Espresso API로 대체합니다.

Espresso API는 테스트 작성자가 애플리케이션과 상호작용하는 동안 수행 - UI 요소 찾기 및 상호작용 공유할 수 있습니다. 동시에 프레임워크는 활동에 직접 액세스하는 것을 방지합니다. 애플리케이션 뷰를 계속 볼 수 있기 때문에 UI 스레드 밖에서 이루어지는 것이 테스트 결함의 주요 원인입니다. 따라서 다음과 같은 작업이 가능합니다. Espresso API에서 getView()getCurrentActivity()와 같은 메서드를 볼 수 없습니다. 자체 서브클래스를 구현하여 뷰에서 계속 안전하게 작업할 수 있습니다. ViewActionViewAssertion.

API 구성요소

Espresso의 기본 구성요소에는 다음이 포함됩니다.

  • Espresso: 뷰와의 상호작용을 위한 진입점입니다 (onView()onData()). 또한 다음과 같이 뷰에 연결되지 않아도 되는 API를 노출합니다. pressBack() 형식으로 되어 있습니다.
  • ViewMatchers – ViewMatchers를 구현하는 Matcher<? super View> 인터페이스 이 중 하나 이상을 onView() 메서드를 사용하여 현재 뷰 계층 구조 내에서 뷰를 찾습니다.
  • ViewActions – 다음 항목에 전달할 수 있는 ViewAction 객체의 모음입니다. ViewInteraction.perform() 메서드(예: click())
  • ViewAssertions - 여러 인스턴스에 연결할 수 있는 ViewAssertion 객체의 컬렉션 ViewInteraction.check() 메서드를 전달했습니다. 대부분의 경우 뷰 매처를 사용하여 선택할 수 있습니다.

예:

Kotlin

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()))

자바

// withId(R.id.my_view) is a ViewMatcher
// click() is a ViewAction
// matches(isDisplayed()) is a ViewAssertion
onView(withId(R.id.my_view))
    .perform(click())
    .check(matches(isDisplayed()));

뷰 찾기

대부분의 경우 onView() 메서드는 hamcrest 매처를 사용합니다. 현재 보기 내에 있는 하나의 보기와만 일치할 것으로 예상됨 계층 구조로 구성됩니다 매처는 강력하며 이전에 사용한 적이 있는 사용자에게 익숙할 것입니다. Mockito 또는 JUnit을 사용합니다. hamcrest 매처에 익숙하지 않다면 먼저 이 슬라이드와 프레젠테이션 문서를 참조하세요.

원하는 뷰에는 고유한 R.id가 있는 경우가 많으며 단순 withId 매처는 검색 범위를 좁혀야 합니다 그러나 당신이 테스트 개발 시간에 R.id를 확인할 수 없습니다. 예를 들어 R.id가 없거나 R.id가 고유하지 않을 수 있습니다. 이렇게 하면 계측 테스트는 불안정하고 작성하기 복잡합니다. findViewById()를 사용하여 뷰에 액세스할 수 없습니다. 따라서 뷰를 보유하는 활동 또는 프래그먼트의 비공개 멤버에 액세스해야 하거나 알려진 R.id가 있는 컨테이너를 찾아 확인할 수 있습니다.

Espresso는 개발자가 뷰의 범위를 좁힐 수 있도록 하여 이 문제를 깔끔하게 처리합니다. 기존 ViewMatcher 객체 또는 자체 커스텀 객체 사용

R.id로 뷰를 찾는 것은 onView()를 호출하는 것만큼 간단합니다.

Kotlin

onView(withId(R.id.my_view))

자바

onView(withId(R.id.my_view));

때로 R.id 값은 여러 뷰 간에 공유됩니다. 이 경우 특정 R.id를 사용하려고 하면 다음과 같은 예외가 발생합니다. AmbiguousViewMatcherException 예외 메시지를 통해 현재 뷰 계층 구조로 표현되며, 이 뷰 계층 구조를 검색하여 고유하지 않은 R.id와 일치하는 뷰입니다.

java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

뷰의 다양한 속성을 살펴보면 식별 가능한 속성입니다. 위의 예에서 뷰 중 하나에 "Hello!" 이 기능을 이용하면 매처:

Kotlin

onView(allOf(withId(R.id.my_view), withText("Hello!")))

자바

onView(allOf(withId(R.id.my_view), withText("Hello!")));

매처를 역전시키지 않도록 선택할 수도 있습니다.

Kotlin

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))

자바

onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));

ViewMatchers를 참고하세요. Espresso에서 제공하는 뷰 매처의 클래스

고려사항

  • 정상적으로 작동하는 애플리케이션에서 사용자가 상호작용할 수 있는 모든 뷰는 텍스트 또는 콘텐츠 설명이 있어야 합니다. 자세한 내용은 더 많은 사용자를 위해 더욱 접근성 높은 앱 만들기 확인하세요. withText() 또는 withContentDescription()인 경우 접근성 버그로 처리하는 것이 좋습니다.
  • 찾고 있는 하나의 뷰를 찾는 최소 설명 매처를 사용합니다. 정의합니다. 프레임워크가 더 많은 작업을 하게 만들므로 지나치게 지정하지 마세요. 반드시 필요합니다 예를 들어 텍스트만으로 보기를 고유하게 식별할 수 있는 경우 뷰도 TextView에서 할당할 수 있음을 지정할 필요가 없습니다. 많은 경우 뷰의 R.id가 충분해야 합니다.
  • 타겟 뷰가 AdapterView 내부에 있는 경우(예: ListView) GridView 또는 Spinner: onView() 메서드가 작동하지 않을 수 있습니다. 이러한 대신 onData()를 사용해야 합니다.

뷰에 작업 실행

타겟 뷰에 적합한 매처를 찾았으면 수행 메서드를 사용하여 거기에 ViewAction 인스턴스를 수행합니다.

예를 들어 뷰를 클릭하려면 다음을 실행합니다.

Kotlin

onView(...).perform(click())

자바

onView(...).perform(click());

한 번의 perform 호출로 둘 이상의 작업을 실행할 수 있습니다.

Kotlin

onView(...).perform(typeText("Hello"), click())

자바

onView(...).perform(typeText("Hello"), click());

작업 중인 뷰가 ScrollView (세로 또는 뷰가 가로로 놓여 있어야 하는 작업보다 먼저 scrollTo()와 함께 표시됩니다(예: click()typeText()). 이 는 다른 작업으로 진행하기 전에 뷰가 표시되는지 확인합니다.

Kotlin

onView(...).perform(scrollTo(), click())

자바

onView(...).perform(scrollTo(), click());

ViewActions를 참고하세요. Espresso에서 제공하는 뷰 작업에 사용됩니다.

뷰 어설션 확인

check()를 사용하여 현재 선택된 뷰에 어설션을 적용할 수 있습니다. 메서드를 사용하여 축소하도록 요청합니다. 가장 많이 사용되는 어설션은 matches() 어설션입니다. Kubernetes는 ViewMatcher 객체로, 현재 선택된 뷰의 상태를 어설션합니다.

예를 들어 뷰에 "Hello!" 텍스트가 있는지 확인하려면 다음 코드를 사용합니다.

Kotlin

onView(...).check(matches(withText("Hello!")))

자바

onView(...).check(matches(withText("Hello!")));

"Hello!"가 뷰의 콘텐츠인지 어설션하려는 경우 다음은 잘못된 사례로 간주됩니다.

Kotlin

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))

자바

// Don't use assertions like withText inside onView.
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

반면에 "Hello!" 텍스트가 있는 뷰가 다음과 같다고 어설션하려면 예를 들어 뷰 가시성 플래그를 변경한 후 괜찮습니다

뷰 어설션 단순 테스트

이 예에서는 SimpleActivityButtonTextView가 포함되어 있습니다. 이 버튼을 클릭하면 TextView의 콘텐츠가 "Hello Espresso!"로 변경됩니다.

다음은 Espresso를 사용하여 이를 테스트하는 방법입니다.

버튼 클릭

첫 번째 단계는 버튼을 찾는 데 도움이 되는 속성을 찾는 것입니다. 이 SimpleActivity의 버튼에는 예상대로 고유한 R.id가 있습니다.

Kotlin

onView(withId(R.id.button_simple))

자바

onView(withId(R.id.button_simple));

이제 다음과 같이 클릭을 실행합니다.

Kotlin

onView(withId(R.id.button_simple)).perform(click())

자바

onView(withId(R.id.button_simple)).perform(click());

TextView 텍스트 확인

확인할 텍스트가 포함된 TextView에도 고유한 R.id가 있습니다.

Kotlin

onView(withId(R.id.text_simple))

자바

onView(withId(R.id.text_simple));

이제 다음과 같이 콘텐츠 텍스트를 확인합니다.

Kotlin

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))

자바

onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));

어댑터 뷰에서 데이터 로드 확인

AdapterView는 데이터를 동적으로 로드하는 특수한 유형의 위젯입니다. 어댑터입니다. AdapterView의 가장 일반적인 예는 ListView입니다. 따라서 LinearLayout와 같은 정적 위젯과는 달리 AdapterView 하위 요소를 현재 뷰 계층 구조에 로드할 수 있습니다. 간단한 onView() 검색은 현재 로드되지 않은 뷰를 찾지 못합니다.

Espresso는 다음과 같은 별도의 onData() 진입점을 제공하여 이를 처리합니다. 문제가 되는 어댑터 항목을 먼저 로드하여 이전에 포커스를 두었습니다. 모든 하위 요소에 대해 작업을 수행할 수 있습니다

경고: AdapterViewonData() 문제가 있을 수 있습니다. 메서드가 상속 계약을 위반할 경우, 특히 getItem() API 이 경우 최선의 조치는 리팩터링할 필요가 없습니다 그렇게 할 수 없는 경우 일치하는 맞춤 AdapterViewProtocol입니다. 자세한 내용은 기본값 보기 <ph type="x-smartling-placeholder"></ph> Espresso에서 제공하는 AdapterViewProtocols 클래스

어댑터 뷰 단순 테스트

이 단순 테스트는 onData()를 사용하는 방법을 보여줍니다. SimpleActivity에는 커피 음료의 유형을 나타내는 몇 가지 항목이 있는 Spinner입니다. 사용자가 항목이 선택되면 "One %s a day!"로 변경되는 TextView가 있습니다. 여기서 %s는 선택된 항목을 나타냅니다.

이 테스트의 목표는 Spinner를 열고 특정 항목을 선택한 후 TextView에 항목이 포함되어 있는지 확인합니다. Spinner 클래스는 AdapterView의 경우 다음과 같은 경우 onView() 대신 onData()를 사용하는 것이 좋습니다. 표시됩니다.

항목 선택 열기

Kotlin

onView(withId(R.id.spinner_simple)).perform(click())

자바

onView(withId(R.id.spinner_simple)).perform(click());

항목 선택

항목 선택을 위해 Spinner는 콘텐츠가 있는 ListView를 만듭니다. 이 뷰는 매우 길 수 있으며 요소가 뷰에 기여하지 않을 수 있습니다. 계층 구조로 구성됩니다 onData()를 사용하여 원하는 요소를 뷰에 강제로 적용 계층 구조로 구성됩니다 Spinner의 항목은 문자열이므로 항목 하나를 일치시키려고 합니다. 다음과 같습니다."Americano"

Kotlin

onData(allOf(`is`(instanceOf(String::class.java)),
        `is`("Americano"))).perform(click())

자바

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

텍스트가 올바른지 확인

Kotlin

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))))

자바

onView(withId(R.id.spinnertext_simple))
    .check(matches(withText(containsString("Americano"))));

디버깅

Espresso는 테스트 실패 시 유용한 디버깅 정보를 제공합니다.

로깅

Espresso는 모든 뷰 작업을 logcat에 로깅합니다. 예:

ViewInteraction: Performing 'single click' action on view with text: Espresso

뷰 계층 구조

Espresso가 onView() 시 예외 메시지에 뷰 계층 구조를 출력합니다. 있습니다

  • onView()가 타겟 뷰를 찾지 못하면 NoMatchingViewException은 발생합니다. 예외 문자열에서 뷰 계층 구조를 검사하여 일치자가 어떤 보기도 일치하지 않는 이유입니다.
  • onView()가 지정된 매처와 일치하는 뷰를 여러 개 찾으면 AmbiguousViewMatcherException이 발생합니다. 뷰 계층 구조가 인쇄되고 일치된 뷰는 MATCHES 라벨로 표시됩니다.
java.lang.RuntimeException:
androidx.test.espresso.AmbiguousViewMatcherException
This matcher matches multiple views in the hierarchy: (withId: is <123456789>)

...

+----->SomeView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=false, enabled=true,
selected=false, is-layout-requested=false, text=,
root-is-layout-requested=false, x=0.0, y=625.0, child-count=1}
****MATCHES****
|
+------>OtherView{id=123456789, res-name=plus_one_standard_ann_button,
visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true,
window-focus=true, is-focused=false, is-focusable=true, enabled=true,
selected=false, is-layout-requested=false, text=Hello!,
root-is-layout-requested=false, x=0.0, y=0.0, child-count=1}
****MATCHES****

복잡한 뷰 계층 구조 또는 예상치 못한 위젯 동작을 처리할 때 항상 Android 스튜디오의 Hierarchy Viewer 설명해 드리겠습니다.

어댑터 뷰 경고

Espresso는 사용자에게 AdapterView 위젯의 존재에 관해 경고합니다. onView()의 경우 작업에서 NoMatchingViewException이 발생하고 AdapterView 위젯은 가장 일반적인 솔루션은 onData()를 사용하는 것입니다. 예외 메시지에는 어댑터 뷰 목록과 함께 경고가 포함됩니다. 이 정보를 사용하여 타겟 뷰를 로드하는 onData()를 호출할 수 있습니다.

추가 리소스

Android 테스트에서 Espresso를 사용하는 방법에 관한 자세한 내용은 확인할 수 있습니다

샘플