안드로이드 테스트 전용 모듈에서 효과적인 테스트하기

케니스·2023년 1월 18일
1

이 글은 Shubham Garg님의 을 번역한 글입니다.

안드로이드 앱을 다른 모듈로 모듈화하는 것은 코드 유지보수 및 빌드 성능에 이점이 있는지는 모두 알고 있습니다.

따라서 이 글에서는 계측 테스트에서도 동일하게 이 이점을 가져갈 수 있는지에 대해서 얘기해보려고합니다.

안드로이드 라이브러리 모듈과 앱 모듈은 디바이스에서 실행되는 테스트를 작성할 수 있는 androidTest와 JVM에서 로컬로 실행되는 test(unitTest)소스 세트가 있습니다.

Robolectric같은 프레임워크를 사용하거나 안드로이드 프레임워크 의존성이 요구 될 때마다 더 빠르고 쉽게 실행하기 위해서 Mocks/Stubs 를 사용하거나 모든 테스트를 JVM에서 작성하고 싶을 때가 있습니다. 하지만 앱에서는 JVM머신으로 컴파일되지 않은 네이티브 라이브러리를 가질 수 있고 실제 디바이스에서 동작하는 테스트를 작성하고 싶을 때는 androidTest 소스 세트에서 작성해야 합니다.

androidTest 소스 세트에서 테스트 코드를 작성함으로 팀내에서 개발자와 QA 모두 앱의 다양한 시나리오를 자동화 할 수 있습니다. 스크린샷 테스트, 네트워크 테스트, E2E 워크플로 테스트,퍼포먼스 테스트, 기타 등으로 구성된 앱 모듈에서 설정된 androidTest 소스 세트는 수백개에서 수천 개의 테스트로 인해 크기를 증가시킬 수 있고 이로 인해 새로운 테스트를 위한 안드로이드 테스트 APK를 생성하는 것 자체가 일반 APK 생성 시간보다 더 많은 시간이 걸립니다. Hilt나 Dagger 같은 플러그인을 사용한다면 더 상황이 나빠질 수 있습니다. 해당 테스트가 외부 요인 없이 로컬에서 작동해야하는지, 테스트가 특정 장치나 종속성에 의존하는지에 대한 결정하는 것은 어려울 수 있습니다. 어노테이션 효과적으로 관리하기 위해 새로운 어노테이션을 도입해야 할 수도 있습니다.

이것은 androidTest 코드가 시작할 때 관리하기 어려운 이유입니다.

테스트 코드를 모듈로 분리

테스트코드를 유지보수하기 더 쉽게 만드는 방법은 androidTest 코드를 다른 gradle 모듈로 분리하는 것 입니다. 분리하는 방법 중 하나는 별도의 라이브러리 모듈을 만들어서 앱 모듈에 의존성을 포함하는 것입니다. 하지만 이렇게 하는 것은 생각보다 간단하지 않고 Manifest 머지 이슈를 마주할 수도 있습니다. 또한 maintest 소스 세트가 필요하지 않은 새로운 라이브러리 모듈을 만들어야합니다.

안드로이드 테스트 모듈들이 분리되는 모습입니다. 이 모듈은 오직 계측 테스트만을 포함하고 앱 이나 다른 라이브러리 모듈을 의존하고 있는 특수 모듈입니다.

안드로이드 테스트 모듈 만들기

안드로이드 테스트 만을 위한 모듈을 만들기 위해서는 다음과 같은 단계를 따라야합니다.

plugins {
    // This is a test only module.
    // All the tests inside this would run as Android Instrumentation Tests.
    id 'com.android.test'
}
  1. 프로젝트에서 새로운 모듈을 만들고 모듈들의 build.gradle 파일에 'com.android.test' 플러그인을 작성한다

새로운 모듈을 만들 때 안드로이드 스튜디오의 템플릿을 사용하고 라이브러리 모듈을 시작점으로 build.gradle 파일에서 com.android.library를 com.android.test 플러그인으로 교체해줍니다.

  1. 테스트할 프로젝트를 지정합니다
android {
    // Here I am targeting the app module.
    targetProjectPath ':app'

    ....
    // If your app module has different flavors, you will have to declare the same here.
}
  1. dependencies 블록에 일반적으로 프로젝트에서 사용하는 테스트 라이브러리 항목들을 정의합니다.
dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.5.1'
    implementation 'com.google.android.material:material:1.7.0'
    implementation 'junit:junit:4.13.2'
    implementation 'androidx.test.ext:junit:1.1.5'
    implementation 'androidx.test.espresso:espresso-core:3.5.1'
    implementation project(':app')
}
  1. 테스트-Only 모듈에 첫번 째 테스트를 생성합니다.

모든 테스트 코드는 androidTest 소스 세트가 아닌 main 소스 세트 안에 있어야 합니다.

class ExampleAppInstrumentedTest {

    @get:Rule
    val activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)

    @Test
    fun testAppModuleActivity() {
        Espresso.onView(ViewMatchers.withId(com.example.testonlymodule.R.id.main_activity_view))
            .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
    }
}

계측 테스트를 Gradle 커맨드를 통해 실행해볼 수 있습니다.(moduleName은 실제 모듈의 이름으로 변경해야 합니다.)

./gradlew :{moduleName}:connectedDebugAndroidTest

지금까지가 계측 테스트를 위해 테스트-Only 모듈을 설정하는 방법입니다.

전체 구현된 예제를 보려면 아래 저장소를 참고하세요.

https://github.com/shubhamgarg1/TestOnlyModule

Jacoco를 사용해서 앱의 모듈들에서 코드 커버리지를 생성할 수 있고 이런 테스트들을 헤드리스 에뮬레이터를 사용해서 실행할 수 있습니다(최소 AGP 버전 7.3.1)

보너스 : 다른 테스트 모듈간의 코드 공유

테스트 모듈간에 코드를 공유하는 방법 모든 테스트 모듈들이 포함된 shared test 모듈을 생성하는것 입니다. shared test 모듈은 안드로이드가 테스트-Only 모듈이 다른 모듈에 포함하는 것을 허용하지 않으므로 라이브러리 모듈이어야 합니다.

unit test와 android Test 소스세트들 사이에서 코드를 공유할 때와 동일한 개념으로 사용됩니다. 이전에는 2개의 소스세트에 공통 패키지를 포함하여 사용할 수 있었지만 Android Studio Chipmunk에서는 더 이상 불가능합니다.

네트워크 테스트와 퍼포먼스 테스트 모듈간의 공유가 가능한 유틸리티가 있다고 가정했을 때 Share Test Utility 모듈을 만들어서 공유할 수 있습니다.

결론

com.android.test 플러그인을 사용하여 테스트 코드를 정리하고 더 유지보수가 쉽고 유연하게 도와주는 것을 알 수 있습니다. 이런 분리된 형태는 모든 커밋 후에 일부 테스트 모듈을 실행할지, 매일 한 번 테스트 모듈을 실행할지에 대한 결정을 도와줍니다.

profile
노력하는 개발자입니다.

0개의 댓글