ローカル単体テストを作成する

ローカルテストは、Android デバイスやエミュレータではなく、独自のワークステーションで直接実行されます。そのため、Android デバイスではなく、ローカルの Java 仮想マシン(JVM)を使用してテストを実行します。ローカルテストを使用すると、アプリのロジックをより迅速に評価できます。ただし、Android フレームワークを操作できないため、実行できるテストの種類に制限があります。

単体テストでは、コードの小さなセクション(テスト対象ユニット)の動作を検証します。この処理は、該当するコードを実行して結果をチェックすることで行われます。

通常、単体テストはシンプルですが、テスト対象ユニットがテストのしやすさを念頭に置いて設計されていない場合、セットアップに問題が生じる可能性があります。

  • 確認するコードは、テストからアクセス可能である必要があります。たとえば、プライベート メソッドを直接テストすることはできません。代わりに、公開 API を使用してクラスをテストします。
  • 単体テストを独立して実行するには、テスト対象の単体テストの依存関係を、フェイクやその他のテストダブルなど、管理対象コンポーネントに置き換える必要があります。これは、コードが Android フレームワークに依存している場合は特に問題になります。

Android での一般的な単体テスト戦略については、テスト項目をご覧ください。

ローカルテストの場所

デフォルトでは、ローカル単体テストのソースファイルは module-name/src/test/ に配置されています。Android Studio で新しいプロジェクトを作成するときに、このディレクトリはすでに存在します。

テスト用の依存関係の追加

また、JUnit テスト フレームワークによって提供される標準 API を使用するように、プロジェクトのテスト依存関係を構成する必要があります。

これを行うには、アプリのモジュールの build.gradle ファイルを開き、次のライブラリを依存関係として指定します。testImplementation 関数を使用して、それらがアプリではなくローカルのテスト ソースセットに適用されることを示します。

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

ローカル単体テストクラスを作成する

ローカル単体テスト クラスは、JUnit 4 テストクラスとして記述します。

そのためには、1 つ以上のテストメソッドを含むクラスを作成します。通常は module-name/src/test/ 内にあります。テストメソッドは @Test アノテーションで始まり、テストするコンポーネントの単一の側面を検証して実行するためのコードが含まれています。

次の例は、ローカル単体テストクラスの実装方法を示しています。テストメソッド emailValidator_correctEmailSimple_returnsTrue() は、アプリ内のメソッドである isValidEmail() の検証を試みます。isValidEmail() も true を返した場合、テスト関数は true を返します。

Kotlin


import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

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

}

Java


import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

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

アプリ内のコンポーネントが期待される結果を返すかどうかを評価する、読み取り可能なテストを作成する必要があります。junit.AssertHamcrestTruth などのアサーション ライブラリを使用することをおすすめします。上のスニペットは、junit.Assert の使用方法の例です。

モック可能な Android ライブラリ

ローカル単体テストを実行すると、Android Gradle プラグインには、プロジェクトで使用されているバージョンに合わせて、Android フレームワークのすべての API を含むライブラリが含まれます。このライブラリにはこれらの API のすべてのパブリック メソッドとクラスが保持されていますが、メソッド内のコードは削除されました。いずれかのメソッドにアクセスすると、テストから例外がスローされます。

これにより、Context などの Android フレームワーク内のクラスを参照する際に、ローカルテストを作成できます。さらに、Android クラスでモック フレームワークを使用できるようになります。

Android 依存関係のモック

よくある問題は、クラスが文字列リソースを使用していることです。文字列リソースを取得するには、Context クラスの getString() メソッドを呼び出します。ただし、ローカルテストでは Android フレームワークに属する Context やそのメソッドを使用できません。getString() の呼び出しがクラスから移動するのが理想的ですが、これは必ずしも現実的ではありません。この問題を解決するには、getString() メソッドが呼び出されたときに常に同じ値を返す Context のモックまたはスタブを作成します。

Mockable Android ライブラリと、MockitoMockK などのモック フレームワークを使用すると、単体テスト内の Android クラスのモックの動作をプログラミングできます。

Mockito を使用してローカル単体テストにモック オブジェクトを追加するには、次のプログラミング モデルに従います。

  1. テスト環境を設定するの説明に従って、Mockito ライブラリの依存関係を build.gradle ファイルに含めます。
  2. 単体テストクラス定義の先頭に、@RunWith(MockitoJUnitRunner.class) アノテーションを追加します。このアノテーションは、フレームワークの使用が正しいことを検証し、モック オブジェクトの初期化を簡素化するように Mockito テストランナーに指示します。
  3. Android の依存関係のモック オブジェクトを作成するには、フィールド宣言の前に @Mock アノテーションを追加します。
  4. 依存関係の動作をスタブするには、when() メソッドと thenReturn() メソッドを使用して、条件を指定し、条件が満たされたときに値を返すようにします。

次の例は、Mockito-Kotlin で作成された Kotlin のモックの Context オブジェクトを使用する単体テストの作成方法を示しています。

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

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

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

Mockito フレームワークの使用方法については、Mockito API リファレンスと、サンプルコードSharedPreferencesHelperTest クラスをご覧ください。Android テスト Codelab もお試しください。

エラー: 「Method ... not mocked」

Error: "Method ... not mocked メッセージを含むメソッドにアクセスしようとすると、Mockable Android ライブラリは例外をスローします。

スローされた例外がテストで問題となる場合は、メソッドが戻り値の型に応じて null またはゼロを返すように動作を変更できます。これを行うには、Groovy でプロジェクトの最上位の build.gradle ファイルに次の構成を追加します。

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