로컬 단위 테스트 빌드

테스트를 더 빠르게 실행해야 하고 실제 기기에서 테스트를 실행함으로써 얻는 충실도와 신뢰도가 필요하지 않은 경우 로컬 단위 테스트를 사용하여 앱 로직을 평가할 수 있습니다. 이 접근 방식에서는 일반적으로 Robolectric 또는 모의 프레임워크(예: Mockito)를 사용하여 종속성 관계를 처리합니다. 일반적으로 테스트와 관련된 종속 항목 유형에 따라 사용하는 도구가 결정됩니다.

  • Android 프레임워크 종속 항목, 특히 프레임워크와의 복잡한 상호작용을 만드는 종속 항목이 있다면 Robolectric을 사용하여 프레임워크 종속 항목을 포함하는 것이 더 좋습니다.
  • 테스트에 최소한의 Android 프레임워크 종속 항목이 있거나 테스트가 고유한 객체에만 종속되어 있다면 Mockito와 같은 모의 프레임워크를 사용하여 모의 종속 항목을 포함하는 것이 좋습니다.

테스트 환경 설정

Android 스튜디오 프로젝트에서는 로컬 단위 테스트의 소스 파일을 module-name/src/test/java/에 저장해야 합니다. 이 디렉터리는 새 프로젝트를 만들 때 이미 존재합니다.

또한 JUnit 4 프레임워크에서 제공하는 표준 API를 사용하도록 프로젝트의 테스트 종속성을 구성해야 합니다. 테스트가 Android 종속 항목과 상호작용해야 하면 Robolectric 또는 Mockito 라이브러리를 포함하여 로컬 단위 테스트를 단순화합니다.

앱의 최상위 수준 build.gradle 파일에서 다음 라이브러리를 종속 항목으로 지정합니다.

    dependencies {
        // Required -- JUnit 4 framework
        testImplementation 'junit:junit:4.12'
        // Optional -- Robolectric environment
        testImplementation 'androidx.test:core:1.0.0'
        // Optional -- Mockito framework
        testImplementation 'org.mockito:mockito-core:1.10.19'
    }
    

로컬 단위 테스트 클래스 만들기

로컬 단위 테스트 클래스는 JUnit 4 테스트 클래스로 작성해야 합니다. JUnit은 가장 인기 있고 널리 사용되는 자바용 단위 테스트 프레임워크입니다. JUnit 4에서는 다음을 실행할 필요가 없으므로 JUnit 4를 사용하면 선행 버전보다 더 명확하고 유연한 방식으로 테스트를 작성할 수 있습니다.

  • junit.framework.TestCase 클래스를 확장합니다.
  • 테스트 메서드 이름 앞에 'test' 키워드를 붙입니다.
  • junit.framework 또는 junit.extensions 패키지의 클래스를 사용합니다.

기본 JUnit 4 테스트 클래스를 만들려면 하나 이상의 테스트 메서드를 포함하는 클래스를 만드세요. 테스트 메서드는 @Test 주석으로 시작하며 테스트할 구성요소의 단일 기능을 사용해 보고 확인하기 위한 코드를 포함합니다.

다음 예는 로컬 단위 테스트 클래스를 구현하는 방법을 보여줍니다. 테스트 메서드 emailValidator_CorrectEmailSimple_ReturnsTrue는 테스트 중인 앱의 isValidEmail() 메서드가 올바른 결과를 반환하는지 확인합니다.

Kotlin

    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    class EmailValidatorTest {
        @Test
        fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue()
        }
    }
    

자바

    import com.google.common.truth.Truth.assertThat;
    import org.junit.Test;

    public class EmailValidatorTest {
        @Test
        public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue();
        }
    }
    

앱의 구성요소가 예상 결과를 반환하는지 여부를 평가하며 읽을 수 있는 테스트를 만들려면 앞의 예와 같이 Truth 라이브러리 및 Android 어설션의 클래스를 사용하는 것이 좋습니다. Truth 및 Android 어설션이 지원하는 로직 유효성 검사의 유형을 자세히 알아보려면 더 읽기 쉬운 어설션을 만드는 방법을 설명하는 섹션을 참조하세요.

그러나 junit.Assert 메서드 또는 Hamcrest 매처(예: is()equalTo() 메서드)를 사용하여 예상 결과와 실제 결과를 비교하는 것이 더 편하다면 대신 이러한 라이브러리를 사용해도 됩니다.

참고: Hamcrest는 Espresso의 ViewMatcher 클래스와 같은 매처를 구성할 때 여전히 선호되는 라이브러리입니다.

프레임워크 종속 항목 포함

테스트가 여러 Android 프레임워크 종속성과 상호작용하거나 복잡한 방식으로 이러한 종속성과 상호작용하는 경우 AndroidX 테스트에서 제공하는 Robolectric 아티팩트를 사용하세요. Robolectric은 로컬 JVM 또는 실제 기기에서 실제 Android 프레임워크 코드 및 가짜 네이티브 프레임워크 코드를 실행합니다.

다음 예는 Robolectric을 사용하는 단위 테스트를 만드는 방법을 보여줍니다.

app/build.gradle

    android {
        // ...
        testOptions {
            unitTests.includeAndroidResources = true
        }
    }
    

MyLocalUnitTestClass

Kotlin

    import android.content.Context
    import androidx.test.core.app.ApplicationProvider
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    private const val FAKE_STRING = "HELLO_WORLD"

    class UnitTestSample {
        val context = ApplicationProvider.getApplicationContext<Context>()

        @Test fun readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            val myObjectUnderTest = ClassUnderTest(context)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING)
        }
    }
    

자바

    import android.content.Context;
    import androidx.test.core.app.ApplicationProvider;
    import org.junit.Test;

    import static com.google.common.truth.Truth.assertThat;

    public class UnitTestSampleJava {
        private static final String FAKE_STRING = "HELLO_WORLD";
        private Context context = ApplicationProvider.getApplicationContext();

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(context);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING);
        }
    }
    

Android Builder 클래스 포함

Robolectric 환경 또는 실제 기기에서 실행하는 로컬 단위 테스트를 만드는 경우 AndroidX 테스트에서 제공하는 빌더를 여러 일반 프레임워크 클래스에 사용할 수 있습니다. 이러한 빌더를 사용하면 모의 또는 리플렉션을 사용하지 않고도 다음 클래스의 인스턴스를 만들 수 있습니다.

Parcelable 유틸리티 클래스 사용

또한 라이브러리는 Parcelable 객체용 유틸리티 클래스를 제공합니다. 이 클래스는 Creator 객체를 제공하여 지정된 Parcelable 객체를 언마샬링한 후 중복 Parcelable 객체를 마샬링합니다.

참고: 언마샬링/재마샬링 작업이 성공했는지 확인하는 것은 Parcelables.forceParcel()을 호출하는 메서드에 달려 있습니다.

모의 종속 항목 포함

기본적으로 Gradle용 Android 플러그인은 실제 코드를 포함하지 않는 android.jar 라이브러리의 수정된 버전에 로컬 단위 테스트를 실행합니다. 대신, 단위 테스트의 Android 클래스에 관한 메서드 호출에서 예외가 발생합니다. 테스트가 코드만 테스트하고 명시적으로 빌드하거나 모의 개체를 생성하지 않은 Android 플랫폼의 특정 동작에 종속되지 않도록 하기 위한 것입니다.

Android 종속 항목 모의

최소 Android 종속 항목이 있는데 앱 내에서 구성요소와 종속 항목 간의 특정 상호작용을 테스트해야 한다면 모의 프레임워크를 사용하여 코드에서 외부 종속 항목을 스터브하세요. 이렇게 하면 구성요소가 예상대로 종속성과 상호작용하는지 쉽게 테스트할 수 있습니다. Android 종속성을 모의 개체로 대체하여 해당하는 종속성의 올바른 메서드가 호출되는지 확인하는 동안 나머지 Android 시스템에서 단위 테스트를 격리할 수 있습니다. 자바(버전 1.9.5 이상)용 Mockito 모의 프레임워크는 Android 단위 테스트와의 호환성을 제공합니다. Mockito를 사용하여 호출 시 일부 특정 값을 반환하도록 모의 개체를 구성할 수 있습니다.

이 프레임워크를 사용하여 로컬 단위 테스트에 모의 객체를 추가하려면 다음 프로그래밍 모델을 따르세요.

  1. 테스트 환경 설정에 설명된 것처럼 Mockito 라이브러리 종속 항목을 build.gradle 파일에 포함합니다.
  2. 단위 테스트 클래스 정의의 시작 부분에 @RunWith(MockitoJUnitRunner.class) 주석을 추가합니다. 이 주석은 프레임워크 사용이 올바르고 모의 객체의 초기화를 단순화하는지 검증하도록 Mockito 테스트 실행기에 알립니다.
  3. Android 종속 항목의 모의 객체를 만들려면 필드 선언 앞에 @Mock 주석을 추가합니다.
  4. 종속 항목의 동작을 스터브하기 위해 when()thenReturn() 메서드를 사용하여 조건 및 조건 충족 시 반환 값을 지정할 수 있습니다.

다음 예는 모의 Context 객체를 사용하는 단위 테스트를 만드는 방법을 보여줍니다.

Kotlin

    import android.content.Context
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test
    import org.junit.runner.RunWith
    import org.mockito.Mock
    import org.mockito.Mockito.`when`
    import org.mockito.junit.MockitoJUnitRunner

    private const val FAKE_STRING = "HELLO WORLD"

    @RunWith(MockitoJUnitRunner::class)
    class UnitTestSample {

        @Mock
        private lateinit var mockContext: Context

        @Test
        fun readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            `when`(mockContext.getString(R.string.hello_word))
                    .thenReturn(FAKE_STRING)
            val myObjectUnderTest = ClassUnderTest(mockContext)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result, `is`(FAKE_STRING))
        }
    }
    

자바

    import android.content.Context;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.junit.MockitoJUnitRunner;

    import static com.google.common.truth.Truth.assertThat;
    import static org.mockito.Mockito.when;

    @RunWith(MockitoJUnitRunner.class)
    public class UnitTestSample {

        private static final String FAKE_STRING = "HELLO WORLD";

        @Mock
        Context mockContext;

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            when(mockContext.getString(R.string.hello_world))
                    .thenReturn(FAKE_STRING);
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(mockContext);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result, is(FAKE_STRING));
        }
    }
    

Mockito 프레임워크를 사용하는 방법을 자세히 알아보려면 Mockito API 참조샘플 코드SharedPreferencesHelperTest 클래스를 참조하세요. 또한 Android 테스트 Codelab도 사용해 보세요.

오류: "메서드의 ... 모의 개체가 생성되지 않았습니다."

모의 객체를 생성하지 않은 Android SDK의 API를 호출하는 테스트를 실행하면 이 메서드의 모의 객체가 생성되지 않았다는 오류가 표시됩니다. 그 이유는 단위 테스트를 실행하는 데 사용되는 android.jar 파일에 실제 코드가 포함되어 있지 않기 때문입니다(Android SDK의 API는 기기의 Android 시스템 이미지에서만 제공함).

대신, 기본적으로 모든 메서드에서 예외가 발생합니다. 이러한 예외 발생은 단위 테스트가 코드만 테스트하고 Mockito 등을 사용하여 명시적으로 모의 객체를 생성하지 않은 Android 플랫폼의 특정 동작에 종속되지 않는지 확인하기 위한 것입니다.

발생한 예외가 테스트에 문제가 되면 프로젝트의 최상위 수준 build.gradle 파일에 다음 구성을 추가하여 메서드가 대신 null 또는 0을 반환하도록 동작을 변경할 수 있습니다.

    android {
      ...
      testOptions {
        unitTests.returnDefaultValues = true
      }
    }
    

주의: returnDefaultValues 속성을 true로 설정할 때는 주의해야 합니다. null/0 반환 값은 테스트에서 회귀를 초래할 수 있기 때문입니다. 이러한 회귀는 디버깅하기 어렵고 실패한 테스트를 통과시킬 수 있습니다. 이러한 설정은 최후의 수단으로만 사용하세요.

로컬 단위 테스트 실행

로컬 단위 테스트를 실행하려면 다음 단계를 따르세요.

  1. 툴바에서 Sync Project 를 클릭하여 프로젝트가 Gradle과 동기화되도록 합니다.
  2. 다음 방법 중 하나로 테스트를 실행합니다.
    • 단일 테스트를 실행하려면 Project 창을 연 후 테스트를 마우스 오른쪽 버튼으로 클릭하고 Run 을 클릭합니다.
    • 클래스의 모든 메서드를 테스트하려면 테스트 파일에 있는 클래스 또는 메서드를 마우스 오른쪽 버튼으로 클릭하고 Run 을 클릭합니다.
    • 디렉터리에 있는 모든 테스트를 실행하려면 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 Run tests 를 선택합니다.

Gradle용 Android 플러그인이 기본 디렉터리(src/test/java/)에 있는 로컬 단위 테스트 코드를 컴파일하고 테스트 앱을 빌드하며 기본 테스트 실행기 클래스를 사용하여 로컬로 실행합니다. 그러면 Android 스튜디오에서 Run 창에 결과를 표시합니다.