Android 앱 테스트의 기본사항

이 페이지에서는 주요 권장사항과 이점을 포함하여 Android 앱 테스트의 핵심 원칙을 간략히 설명합니다.

테스트의 이점

테스트는 앱 개발 프로세스에서 빼놓을 수 없는 부분입니다. 앱 테스트를 일관되게 실행하여 앱을 공개적으로 출시하기 전에 앱의 정확성, 기능 동작 및 사용성을 확인할 수 있습니다.

앱을 탐색하여 수동으로 앱을 테스트할 수 있습니다. 다양한 기기와 에뮬레이터를 사용하고, 시스템 언어를 변경하고, 모든 사용자 오류를 생성하거나 모든 사용자 흐름을 순회하려고 할 수 있습니다.

그러나 수동 테스트는 성능이 저하되므로 앱 동작의 회귀를 간과하기 쉽습니다. 자동 테스트에는 자동으로 테스트를 실행하는 도구를 사용하는 것이 포함됩니다. 이 도구는 더 빠르고 반복 가능하며 일반적으로 개발 프로세스 초기에 앱에 관한 보다 실행 가능한 의견을 제공합니다.

Android의 테스트 유형

모바일 애플리케이션은 복잡하며 여러 환경에서 잘 작동해야 합니다. 따라서 다양한 유형의 테스트가 있습니다.

제목

예를 들어 제목에 따라 다양한 유형의 테스트가 있습니다.

  • 기능 테스트: 앱이 제대로 기능하나요?
  • 성능 테스트: 빠르고 효율적으로 실행하나요?
  • 접근성 테스트: 접근성 서비스와 원활하게 작동하나요?
  • 호환성 테스트: 모든 기기 및 API 수준에서 원활하게 작동하나요?

범위

테스트는 크기 또는 격리 정도에 따라서도 다릅니다.

  • 단위 테스트 또는 소규모 테스트는 메서드나 클래스와 같이 앱의 매우 작은 부분만 확인합니다.
  • 엔드 투 엔드 테스트 또는 대규모 테스트는 전체 화면이나 사용자 흐름과 같이 앱의 큰 부분을 동시에 확인합니다.
  • 중간 테스트는 중간에 있고 두 개 이상의 단원 간의 통합을 확인합니다.
테스트는 작음, 중간, 큼이 있습니다.
그림 1: 일반적인 애플리케이션의 테스트 범위

테스트를 분류하는 방법에는 여러 가지가 있습니다. 그러나 앱 개발자에게 가장 중요한 차이점은 테스트가 실행되는 위치입니다.

계측 테스트와 로컬 테스트 비교

Android 기기나 다른 컴퓨터에서 테스트를 실행할 수 있습니다.

  • 계측 테스트는 실제 또는 에뮬레이션된 Android 기기에서 실행됩니다. 이 앱은 명령어를 삽입하고 상태를 읽는 테스트 앱과 함께 빌드 및 설치됩니다. 계측 테스트는 일반적으로 앱을 실행한 후 상호작용하는 UI 테스트입니다.
  • 로컬 테스트는 개발 머신이나 서버에서 실행되므로 호스트 측 테스트라고도 합니다. 일반적으로 크기가 작고 빠르며, 테스트 대상과 앱의 나머지 부분을 분리합니다.
테스트는 기기에서 계측 테스트로 실행하거나 개발 머신에서 로컬 테스트로 실행할 수 있습니다.
그림 2: 실행 위치에 따른 다양한 유형의 테스트

모든 단위 테스트가 로컬인 것은 아니며 모든 엔드 투 엔드 테스트가 기기에서 실행되는 것은 아닙니다. 예를 들면 다음과 같습니다.

  • 대규모 로컬 테스트: Robolectric과 같이 로컬에서 실행되는 Android 시뮬레이터를 사용할 수 있습니다.
  • 소규모 계측 테스트: 코드가 SQLite 데이터베이스와 같은 프레임워크 기능에서 잘 작동하는지 확인할 수 있습니다. 이 테스트를 여러 기기에서 실행하여 여러 버전의 SQLite와의 통합을 확인할 수 있습니다.

다음 스니펫은 요소를 클릭하고 다른 요소가 표시되는지 확인하는 계측 UI 테스트에서 UI와 상호작용하는 방법을 보여줍니다.

Espresso

// When the Continue button is clicked
onView(withText("Continue"))
    .perform(click())

// Then the Welcome screen is displayed
onView(withText("Welcome"))
    .check(matches(isDisplayed()))

Compose UI

// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()

// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

이 스니펫은 ViewModel (로컬, 호스트 측 테스트)의 단위 테스트의 일부를 보여줍니다.

// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)

// When data is loaded
viewModel.loadData()

// Then it should be exposing data
assertTrue(viewModel.data != null)

테스트 전략 정의

앱이 호환되는 모든 기기에서 앱의 모든 코드 줄을 테스트하는 것이 가장 좋습니다. 안타깝게도 이 접근 방식은 너무 느리고 비용이 많이 듭니다.

좋은 테스트 전략은 테스트의 충실도와 속도, 신뢰성 간의 적절한 균형을 찾습니다. 테스트 환경과 실제 기기의 유사성이 테스트의 충실도를 결정합니다. 더 높은 충실도 테스트는 에뮬레이션된 기기나 실제 기기 자체에서 실행됩니다. 로컬 워크스테이션의 JVM에서 낮은 충실도 테스트를 실행할 수 있습니다. 고충실도 테스트는 속도가 느리고 리소스가 더 필요한 경우가 많으므로 모든 테스트가 고충실도 테스트여야 하는 것은 아닙니다.

불안정 테스트

올바르게 설계되고 구현된 테스트 실행에서도 오류가 발생합니다. 예를 들어 실제 기기에서 테스트를 실행할 때 자동 업데이트가 테스트 도중에 시작되어 실패할 수 있습니다. 코드에서 미묘한 경합 상태가 발생하는 경우는 아주 드물게 있을 수 있습니다. 시간을 100% 통과하지 못하는 테스트는 불안정합니다.

테스트 가능한 아키텍처

테스트 가능한 앱 아키텍처에서는 코드가 다양한 부분을 격리된 상태로 쉽게 테스트할 수 있는 구조를 따릅니다. 테스트 가능한 아키텍처에는 더 나은 가독성, 유지관리성, 확장성, 재사용성과 같은 다른 장점이 있습니다.

테스트할 수 없는 아키텍처에서는 다음을 생성합니다.

  • 테스트가 더 크고 느리며 불안정합니다. 단위 테스트를 할 수 없는 클래스는 더 큰 통합 테스트나 UI 테스트로 처리해야 할 수도 있습니다.
  • 다양한 시나리오를 테스트할 기회가 더 적음 테스트가 클수록 속도가 느려지므로 앱의 가능한 모든 상태를 테스트하는 것은 비현실적일 수 있습니다.

아키텍처 가이드라인에 관한 자세한 내용은 앱 아키텍처 가이드를 참고하세요.

분리에 대한 접근 방식

나머지에서 함수, 클래스 또는 모듈의 일부를 추출할 수 있다면 테스트하는 것이 더 쉽고 효과적입니다. 이러한 방식은 분리라고 하며, 테스트 가능한 아키텍처에서 가장 중요한 개념입니다.

일반적인 디커플링 기법은 다음과 같습니다.

  • 앱을 프레젠테이션, 도메인, 데이터와 같은 레이어로 분할합니다. 앱을 기능당 하나씩 모듈로 분할할 수도 있습니다.
  • 활동과 프래그먼트와 같이 종속 항목이 큰 항목에는 로직을 추가하지 마세요. 이러한 클래스를 프레임워크의 진입점으로 사용하고 UI 및 비즈니스 로직을 컴포저블, ViewModel, 도메인 레이어와 같은 다른 곳으로 이동합니다.
  • 비즈니스 로직이 포함된 클래스에는 직접적인 프레임워크 종속 항목을 피하세요. 예를 들어 ViewModel에서 Android 컨텍스트를 사용하지 마세요.
  • 종속 항목을 쉽게 교체합니다. 예를 들어 구체적인 구현 대신 인터페이스를 사용합니다. DI 프레임워크를 사용하지 않더라도 종속 항목 삽입을 사용하세요.

다음 단계

테스트해야 하는 이유와 두 가지 기본 테스트 유형을 알았으니 이제 테스트할 항목을 읽어보세요.

첫 번째 테스트를 만들고 직접 학습하려면 테스트 Codelab을 확인하세요.