반응형

Espresso API는 테스트 작성자가 사용자의 입장에서 어떻게 애플리케이션과 상호작용 할 것 인가에 대해 생각하도록 해줍니다.

 

테스트가 디바이스의 UI Thread에서 작동되기 때문에 Espresso는 액티비티나 뷰에 대하여 직접적인 접근하는 것을 방지합니다.

따라서, Espresso에는 getView()getCurrentActivity() 같은 메소드가 없습니다.
테스트 작성자는 그저 ViewActionViewAssertion 를 상속하는 커스텀 클래스를 만들어서 안전하게 테스트하도록 해야 합니다.

 

API Components Overview

Espresso의 메인 컴포넌트는 다음과 같습니다.

  • Espresso
    • onView() 와 onData() 를 통해 뷰와 상호작용 할 수 있는 엔트리 포인트입니다.
    • 뷰에 대한 접근이 필수는 아니며, pressBack() 같은 메소드도 있습니다.
  • ViewMatcher
    • Matcher<? super View> 인터페이스를 구현한 클래스들의 집합입니다.
    • onView()의 파라미터로 사용되며, 예시로 withId() 등이 있습니다.
  • ViewActions
    • ViewAction 인터페이스를 구현한 클래스들의 집합입니다.
    • ViewInteraction.perform() 메소드의 파라미터로 쓰이며, 예시로 click() 등이 있습니다.
  • ViewAssertions
    • ViewAssertion 인터페이스를 구현한 클래스들의 집합입니다.
    • ViewInteraction.check() 메소드의 파라미터로 쓰이며, 예시로 matches() 등이 있습니다.

 

예시

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

 

ViewMatchers - Find a View

대다수의 경우에 onView() 메소드는 하나의 View에 매칭되는 hamcrest matcher를 취합니다.
(여기서 hamcrest 란, 'match' 규칙이 정의된 matcher 라이브러리로, 주로 테스트 프레임워크에서 사용됩니다.)

그림 1. hamcrest matcher 라이브러리

 

Matcher는 Mockito 나 JUnit 을 사용 해보신 분이라면 쉽게 이해하실 수 있을 겁니다.

대표적인 메소드로는 withId(), withText() 등이 있으며 이외에 메소드를 더 확인하고 싶다면 Developer 사이트를 참고하면 되겠습니다.

 

어떤 View를 찾기 위해서는, onView() 메소드와 함께 ViewMatcher를 사용해야 합니다. 만약 AdapterView 를 사용하고 있다면 onView() 메소드 대신 onData() 메소드를 사용하면 됩니다.

 

onView() 메소드는 ViewInteraction 객체를 리턴합니다. (onData() 메소드는 DataIneraction 객체)

 

다음은 Espresso의 대표적인 matcher 들을 살펴보겠습니다.

  • withId(int id) : 파라미터로 받은 id와 resource ID가 일치하는 View 를 탐색합니다.
  • withText(String text) : 파라미터로 받은 문자열과 일치하는 Text를 가진 View 를 탐색합니다.
  • allOf(Matcher matcher) : 여러 matcher를 혼합할 수 있습니다. 만약 특정 View 를 제외하고 싶다면 not()을 추가로 사용할 수도 있습니다.
    예 - onView(allOf(withId(R.id.button_login), not(withText("Logout "))));

R.id 의 경우 유니크 하지 않을 수 있습니다.**
이 경우 AmbiguousViewMatcherException을 발생 시킵니다.


따라서 non-unique R.id 를 통해 View를 찾는 경우에는 특정 Activity나 Fragment를 통해 찾고자 하는 View가 있는 컨테이너를 찾아 거기로부터 find 할 수 있게끔 해줘야 테스트의 안정성이 높아집니다.
(또는, allOf()를 이용합니다.)

 

ViewActions - Perform an action on a view

ViewMatcher를 통해 타겟 View 를 찾았다면, onView()(또는 onData()) 가 리턴한 ViewInteraction(또는 DataInteraction) 의 perform() 메소드와 ViewAction 객체를 통해 타겟 View 에 특정 행동을 취할 수 있습니다.

 

대표적인 action 들을 살펴보겠습니다. 메소드 명이 명확하기 때문에 자세한 설명은 생략합니다.

  • click()
  • typeText()
  • pressKey()
  • clearText()
  • scrollTo()

예시 )

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

이외에도 더 많은 메소드를 확인하고 싶다면 역시나 Developer 사이트를 참고하면 되겠습니다.

 

perfrom() 메소드 또한 onView() 와 마찬가지로 ViewIneraction 객체를 리턴합니다.
이는 메소드 체이닝 방식으로 더 많은 액션을 취할 수 있음을 의미합니다.

 

ViewAssertions - Verifying test results

앞서 onView() 또는 perform() 메소드 등에서 리턴된 ViewIneraction 객체에는 check() 메소드가 있습니다. 이 ViewIneraction.check() 메소드는 ViewAssertions 객체를 사용하여 테스트 결과를 확인합니다.

 

ViewAssertions 객체에서 대표적으로 제공하는 메소드는 다음과 같습니다.

  • matches() - 파라미터로 ViewMatchers 객체가 사용됩니다.
  • doesNotExist()

참고로, Assertion 의 역할을 onView() 에서 해결하는 것은 좋은 예시가 아닙니다.

 

예를 들어 특정 View가 "Hello!" 라는 문자열을 갖는지 테스트 할 때에 좋은 예와 나쁜 예를 비교 해보겠습니다.

좋은 예시 )

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

나쁜 예시 )

onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));

 

ActivityTestRule

Android UI Test 에서는 유닛 테스트의 @Before 처럼 각 테스트마다 전처리 해주어야 할 작업들을 @Rule 어노테이션을 통해 설정할 수 있습니다. @Rule 의 대표적인 사용처인 ActivityTestRule 클래스는 단일 액티비티에서 테스트 할 때에 사용됩니다.

 

생성자를 통해 액티비티가 테스트가 수행될 때 launch 된 상태로 시작할 것인지, 테스트 이후 별개로 액티비티를 실행하는 코드를 작성할 것인지 선택할 수 있습니다.

 

ActivityTestRule 객체를 통해서 액티비티를 실행하거나 종료할 수 있습니다.

  • launchActivity(Intent) : 파라미터로 넘어온 Intent 를 통해 액티비티를 실행
  • finishActivity() : 액티비티 종료
  • getActivity() : 현재 액티비티의 인스턴스

 

@Rule 어노테이션은 @Before 보다 먼저 수행됩니다.
그리고 테스트가 @After 어노테이션이 달린 메소드까지 수행을 끝마친 후에야 제거됩니다.

 

샘플

이번에는 앞서 배운 4가지를 모두 활용하여 테스트 해보는 샘플 테스트를 작성 해보겠습니다.

SimpleActivity 에 Button 과 TextView 가 각각 1개씩 있고, 버튼을 클릭하면 TextView 의 텍스트가 "Hello Espresso!" 로 바뀌도록 구현 했다고 가정하겠습니다.

 

이제 이에 대한 테스트를 작성 해보겠습니다.

  1. Test Rule 작성
    SimpleActivity 라는 이름의 액티비티 클래스가 있다고 가정하고, Rule을 작성합니다.

     @Rule
     public ActivityTestRule<SimpleActivity> rule =
                     new ActivityTestRule<>(SimpleActivity.class);
  2. 버튼 클릭
    SimpleActivity 에서 buton_simple 이라는 id 값을 갖는 버튼을 찾아 클릭하는 테스트입니다.

     onView(withId(R.id.button_simple)).perform(click());
  3. 텍스트뷰 검증
    그다음 텍스트뷰의 텍스트가 "Hello Espresso!" 로 변했는지 검증합니다.

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

 

종합

@RunWith(AndroidJUnit4.class)
@MediumTest
public class ExampleEspressoTest {

    @Rule
    public ActivityTestRule<SimpleActivity> activityRule =
            new ActivityTestRule<>(SimpleActivity.class);

    @Test
    public void testHelloEspresso() {
            onView(withId(R.id.button_simple)).perform(click());
            onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
    }
}

 

Filters Annotation

위 예제를 보시면 @MediumTest 라는 어노테이션이 있습니다. 이는 Filter 어노테이션이라고 하는데요.

각각의 종류와 설명은 다음과 같습니다.

  • @FlakyTest

    • concurrency, caching 등 같은 수행에 대해 매번 같은 성공/실패 결과를 내지 않을 수 있는 테스트를 할 때 사용됩니다. 종종 버그를 찾는데 사용됩니다.

  • @LargeTest

    • class or method 수준에 적용되는 어노테이션입니다.

    • 큰 단위의 테스트에 사용되는 어노테이션입니다. (수행 시간 > 1000 ms)

    • 주로 데이터베이스나 네트워크 등 고비용 자원을 사용해야 할 때 사용됩니다.

  • @MediumTest

    • @LargeTest 보다는 작은 단위의 테스트에 사용되는 어노테이션입니다. (수행 시간 < 1000 ms)

    • 단일 컴포넌트나 작은 규모의 컴포넌트들을 테스트할 때 주로 사용됩니다.

    • 데이터베이스나 ContentProvider 등은 접근이 가능하지만 네트워크 사용은 제한됩니다. long-runnging 작업의 경우 mocking 하여 테스트 해야합니다.

  • @SmallTest

    • @MediumTest 보다도 작은 단위의 테스트에 사용되는 어노테이션입니다. (수행 시간 < 200 ms)

    • 사실상 유닛 테스트에 가깝습니다. @SmallTest 는 특정 논리적인 조건문에 집중하여 검증해 나가야 합니다.

    • 파일 시스템, 데이터베이스, 네트워크 등 리소스에 대한 권한이 주어지지 않기 때문에 외부 리소스에 대해서는 mocking 하여 테스트해야 합니다.

  • @RequiresDevice

    • 실제 물리적인 디바이스에서만 테스트가 동작합니다. 에뮬레이터에서는 동작하지 않습니다.

  • @SdkSuppress

    • 테스트가 수행될 minimum or maximum API Level 을 설정할 수 있습니다.

    • class or method 단위에 적용할 수 있습니다.

  • @Suppress

    • 테스트에서 제외시키는 어노테이션입니다.

    • 이 어노테이션이 붙은 테스트는 테스트 되지 않습니다.

 

 

반응형
반응형