Espresso 유휴 리소스

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

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

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

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.
}

Java

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를 사용하는 방법에 관한 자세한 내용은 다음 리소스를 참조하세요.

샘플