6월 3일의 ⁠#Android11: 베타 버전 출시 행사에 참여하세요.

Espresso 유휴 리소스

유휴 리소스는 결과가 UI 테스트의 후속 작업에 영향을 미치는 비동기 작업을 나타냅니다. 유휴 리소스를 Espresso에 등록하면 앱을 테스트할 때 이러한 비동기 작업을 보다 안정적으로 유효성 검사할 수 있습니다.

유휴 리소스가 필요한 경우 식별

Espresso는 정교한 동기화 기능을 제공합니다. 그러나 이 프레임워크 특성은 화면에 콘텐츠를 그리는 View의 서브클래스와 같이 MessageQueue에 메시지를 게시하는 작업에만 적용됩니다.

Espresso는 백그라운드 스레드에서 실행 중인 작업을 포함하여 다른 비동기 작업을 인식하지 못하므로 이러한 상황에서 동기화를 보장할 수 없습니다. Espresso가 앱의 장기 실행 작업을 인식하게 하려면 각 작업을 유휴 리소스로 등록해야 합니다.

앱의 비동기 작업 결과를 테스트할 때 유휴 리소스를 사용하지 않는 경우 테스트의 신뢰성을 향상하기 위해 다음 잘못된 해결 방법 중 하나를 사용해야 할 수도 있습니다.

  • Thread.sleep()에 호출 추가: 테스트에 인위적인 지연을 추가하면 테스트 모음이 실행을 완료하는 데 시간이 더 오래 걸리고 더 느린 기기에서 실행될 때는 경우에 따라 테스트가 실패할 수 있습니다. 또한 앱이 향후 출시 버전에서 시간이 오래 걸리는 비동기 작업을 더 많이 실행해야 할 수 있으므로 이러한 지연은 잘 확장되지 않습니다.
  • 재시도 래퍼 구현: 루프를 사용하여 시간 초과가 발생할 때까지 앱이 비동기 작업을 계속 실행하는지 여부를 반복적으로 확인합니다. 테스트에서 최대 재시도 횟수를 지정하더라도 재실행할 때마다 시스템 리소스, 특히 CPU를 사용합니다.
  • CountDownLatch 인스턴스 사용: 하나 이상의 스레드가 다른 스레드에서 실행되는 특정 수의 작업이 완료될 때까지 대기할 수 있도록 합니다. 이러한 개체를 사용하려면 시간 제한 길이를 지정해야 합니다. 지정하지 않으면 앱이 무기한 차단될 수 있습니다. 또한 래치는 코드에 불필요한 복잡성을 추가하여 유지 관리를 더 어렵게 만듭니다.

Espresso를 사용하면 이러한 신뢰할 수 없는 해결 방법을 테스트에서 삭제하고 대신 앱의 비동기 작업을 유휴 리소스로 등록할 수 있습니다.

일반적인 사용 사례

테스트에서 다음 예와 유사한 작업을 수행할 때 유휴 리소스를 사용해 보세요.

  • 인터넷 또는 로컬 데이터 소스에서 데이터 로드
  • 데이터베이스 및 콜백과의 연결 설정
  • 시스템 서비스 또는 IntentService 인스턴스를 사용하여 서비스 관리
  • 복잡한 비즈니스 로직 실행(예: 비트맵 변환)

특히 이러한 작업이 이후 테스트에서 유효성을 검사하는 UI를 업데이트하는 경우 유휴 리소스를 등록하는 것이 중요합니다.

유휴 리소스 구현 예

다음 목록에는 앱에 통합할 수 있는 유휴 리소스의 몇 가지 구현 예가 설명되어 있습니다.

CountingIdlingResource
활성 작업의 카운터를 유지 관리합니다. 카운터가 0이면 연결된 리소스가 유휴 상태로 간주됩니다. 이 기능은 Semaphore의 기능과 매우 유사합니다. 대부분의 경우 테스트 중 비동기 작업을 관리하는 데는 이 구현으로 충분합니다.
UriIdlingResource
CountingIdlingResource와 유사하지만 리소스가 유휴 상태로 간주되려면 특정 기간 동안 카운터가 0이어야 합니다. 이 추가 대기 기간은 스레드의 앱이 이전 요청에 대한 응답을 수신한 후 즉시 새로 요청할 수 있는 연속 네트워크 요청을 고려합니다.
IdlingThreadPoolExecutor
생성된 스레드 풀 내에서 실행 중인 작업의 총 수를 추적하는 ThreadPoolExecutor의 맞춤 구현입니다. 이 클래스는 CountingIdlingResource를 사용하여 활성 작업의 카운터를 유지 관리합니다.
IdlingScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor의 맞춤 구현입니다. 이 구현은 IdlingThreadPoolExecutor 클래스와 동일한 기능을 제공하지만 나중에 실행되거나 주기적으로 실행되도록 예약된 작업을 추적할 수도 있습니다.

자체 유휴 리소스 만들기

앱 테스트에서 유휴 리소스를 사용할 때 맞춤 리소스 관리 또는 로깅을 제공해야 할 수도 있습니다. 이 경우 이전 섹션에 나열된 구현만으로 충분하지 않을 수 있습니다. 충분하지 않으면 이러한 유휴 리소스 구현 중 하나를 확장하거나 자체 구현을 만들어야 합니다.

자체 유휴 리소스 기능을 구현하는 경우 다음 권장사항, 특히 첫 번째 권장사항에 유의하세요.

유휴 검사 외부에서 유휴 상태로의 전환을 호출합니다.
앱이 유휴 상태가 되면 isIdleNow() 구현 외부에서 onTransitionToIdle()을 호출하세요. 이렇게 하면 Espresso가 주어진 유휴 리소스가 유휴 상태인지 판별하기 위해 불필요한 두 번째 검사를 하지 않습니다.

다음 코드 스니펫은 이 권장사항을 보여줍니다.

Kotlin

    fun isIdle() {
        // DON'T call callback.onTransitionToIdle() here!
    }

    fun backgroundWorkDone() {
        // Background work finished.
        callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

        // Don't do any post-processing work beyond this point. Espresso now
        // considers your app to be idle and moves on to the next test action.
    }
    

자바

    public void isIdle() {
        // DON'T call callback.onTransitionToIdle() here!
    }

    public void backgroundWorkDone() {
        // Background work finished.
        callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

        // Don't do any post-processing work beyond this point. Espresso now
        // considers your app to be idle and moves on to the next test action.
    }
    
필요하기 전에 유휴 리소스를 등록합니다.

유휴 리소스와 관련된 동기화 이점은 Espresso가 해당하는 리소스의 isIdleNow() 메서드를 처음으로 호출한 후에만 적용됩니다.

다음 목록은 이 속성의 예를 보여줍니다.

  • @Before 주석이 달린 메서드에서 유휴 리소스를 등록하면 유휴 리소스가 각 테스트의 첫 번째 줄에 적용됩니다.
  • 테스트 내부에 유휴 리소스를 등록하면 유휴 리소스가 다음 Espresso 기반 작업 중에 적용됩니다. 이 동작은 다음 작업이 유휴 리소스를 등록하는 명령문과 동일한 테스트에 있는 경우에도 계속 발생합니다.
유휴 리소스 사용을 완료한 후 유휴 리소스의 등록을 취소합니다.

시스템 리소스를 절약하려면 유휴 리소스가 더 이상 필요하지 않게 되는 즉시 이 리소스의 등록을 취소해야 합니다. 예를 들어, @Before 주석이 달린 메서드에서 유효 리소스를 등록하는 경우 @After 주석이 달린 해당하는 메서드에서 이 리소스의 등록을 취소하는 것이 가장 좋습니다.

유휴 레지스트리를 사용하여 유휴 리소스를 등록하고 등록 취소합니다.

앱의 유휴 리소스에 이 컨테이너를 사용하여 필요에 따라 반복적으로 유휴 리소스를 등록 및 등록 취소하고 일관된 동작을 계속 관찰할 수 있습니다.

단순 앱 상태만 유휴 리소스 내에서 유지 관리합니다.

예를 들어, 구현하고 등록하는 유휴 리소스에 View 개체의 참조를 포함해서는 안 됩니다.

유휴 리소스 등록

Espresso는 앱의 유휴 리소스를 배치할 수 있는 컨테이너 클래스를 제공합니다. IdlingRegistry라고 하는 이 클래스는 앱에 최소한의 오버헤드를 발생시키는 자체 포함 아티팩트입니다. 이 클래스를 사용하여 앱의 유지보수성 향상을 위한 다음 단계를 실행할 수도 있습니다.

  • 앱 테스트에서 유휴 레지스트리에 포함된 유휴 리소스 대신 IdlingRegistry의 참조를 만듭니다.
  • 각 빌드 변형에 사용하는 유휴 리소스 컬렉션의 차이를 유지합니다.
  • 앱 서비스를 참조하는 UI 구성요소가 아니라 앱 서비스에서 유휴 리소스를 정의합니다.

유휴 리소스를 앱에 통합

여러 가지 다양한 방법으로 앱에 유휴 리소스를 추가할 수 있지만 특히 한 가지 접근 방식은 주어진 유휴 리소스가 나타내는 특정 작업을 계속 지정할 수 있도록 하면서 앱의 캡슐화를 유지합니다.

유휴 리소스를 앱에 추가할 때 등록 및 등록 취소 작업만 테스트에서 실행하고 유휴 리소스 로직 자체는 앱의 프로덕션 코드에 배치할 것을 적극 권장합니다.

이 접근 방식을 따르면 프로덕션 코드에서 테스트 전용 인터페이스를 사용하는 비정상적인 상황이 만들어지지만 이미 있는 코드 주위에 유휴 리소스를 래핑하여 앱의 APK 크기 및 메서드 수를 유지할 수 있습니다.

대체 방법

앱의 프로덕션 코드에 유휴 리소스 로직을 포함하지 않으려는 경우 몇 가지 다른 실행 가능한 통합 전략이 있습니다.

  • Gradle의 제품 버전과 같은 빌드 변형을 만들고 앱의 디버그 빌드에서만 유휴 리소스를 사용하세요.
  • Dagger와 같은 종속성 주입 프레임워크를 사용하여 앱의 유휴 리소스 종속성 그래프를 테스트에 삽입하세요. Dagger 2를 사용하는 경우 주입 자체는 하위 구성요소에서 시작되어야 합니다.
  • 앱 테스트에서 유휴 리소스를 구현하고 해당하는 테스트에서 동기화되어야 하는 앱 구현 파트를 노출하세요.

    주의: 이 디자인 결정은 유휴 리소스의 자체 포함 참조를 만드는 것으로 보이지만 가장 간단한 앱을 제외한 모든 앱에서 캡슐화를 중단합니다.

참고 자료

Android 테스트에서 Espresso를 사용하는 방법에 관한 자세한 내용은 다음 자료를 참조하세요.

샘플