【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

この記事からわかること

  • Android Studio/Kotlinで使えるJUnit使い方
  • テスト駆動開発とは?
  • 単体テスト実装方法
  • アノテーション種類

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

公式リファレンス:Android Studioでテストする

JUnitとは?

公式リファレンス:JUnit
公式リファレンス:JUnit4リポジトリ

JUnitJavaアプリケーションの単体テストを記述、実行するためのフレームワークです。JUnit自体はテスト駆動開発(TDD)と呼ばれる開発手法をサポートするためのツールとして開発されたもので、テストケースの定義やテストの自動実行などテストを行う上で必要な機能が色々用意されています。

単体テストとは?

単体テスト(Unit Test)とは個々のユニット(メソッド、関数、クラスなど)が期待どおりに動作するかどうかを確認するためのテスト方法です。実装した処理自体が間違っていないか、異なる結果を出力しないかを個々にチェックすることが目的になります。

例えば以下のようなテストを行います。

単体テストを実装する上でモックを作成することも多いと思いまずがMockKというモックライブラリを使用すると便利です。

テスト駆動開発(TDD)とは?

テスト駆動開発(TDD)とはプログラムを書く前にテストコードを書く手法のことです。テスト駆動開発では「Red・Green・Refactoring」という3つのステップをサイクルとして取り入れて開発を進めていきます。

このサイクルを繰り返すことによりコードの品質を保ちながら開発を進めることが可能になります。

メリット

デメリット

導入方法

Android StudioでJUnitを導入するためには「build.gradle(Module:app)」にtestImplementation 'junit:junit:4.13.2'を記述します。とはいえAndroid Studioでプロジェクトを立ち上げると最初から導入されているかと思うので明示的に導入する機会は少ないかもしれません。

他にもデフォルトで色々入っているようです。


dependencies {
    // 単体テスト
    testImplementation 'junit:junit:4.13.2'
    // インストルメンテーションテスト(デバイス上またはエミュレータ上で実行するテスト)
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    // UIテスト
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

現在デフォルトで導入されるのはJUnit4ですが、現時点の最新はJUnit5のようです。

JUnitの使い方

JUnitを使用して単体テストを実装する流れを見ていきます。テストコードはViewModelManagerクラスなどの単位で作成していくと管理しやすいです。今回はシンプルなSampleManagerクラスを用意しています。メソッドはaとbを足すaddとaをbで割るdivideの2つが定義されています。


class SampleManager {
    /** aとbを足す */
    fun add(a: Int, b: Int): Int {
        return a + b
    }

    /** aをbで割る */
    fun divide(a: Int, b: Int): Int {
        if (b == 0) throw IllegalArgumentException("Cannot divide by zero")
        return a / b
    }
}

テストコードクラスの自動生成

テストコードクラスはAndroid StudioのGUI操作で自動で生成することが可能です。まずは対象のクラスの上で右クリック>「Generate...」をクリックします。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

続いて「Test...」をクリックします。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

Testing LibraryJUnit4に変更してテスト対象のメソッドをにチェックを入れておきます。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

最後に生成先を指定して「OK」をクリックします。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

(test)ディレクトリの中に対象のテストコードクラスが自動生成されます。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

この時点で生成されたテストコードクラスは中身がまだ空の状態です。


class SampleManagerTest {

    @Test
    fun add() {
    }

    @Test
    fun divide() {
    }
}

単体テストコードを記述する

単体テストコードの記述するポイントは以下の通りです。

テストを実行するためにSampleManagerクラスをインスタンス化してプロパティとして保持しておき各テストメソッドの中で対象のメソッドに対してassertEqualsを使用して期待通りの値になっているかを検証します。


import org.junit.Assert.*
import org.junit.Test

class SampleManagerTest {

    private val manager = SampleManager()

    @Test
    fun testAdd() {
        val result = manager.add(2, 3)
        assertEquals(5, result)
    }

    @Test
    fun testDivide() {
        val result = manager.divide(10, 2)
        assertEquals(5, result)
    }

    /** 例外を発生させるテスト */
    @Test(expected = IllegalArgumentException::class)
    fun testDivideByZero() {
        manager.divide(10, 0)
    }
}

テストの実行

テストを実行するには対象クラスを右クリック>「Run 'テストクラス名'」をクリックします。テストが実行され異常なく成功すると各テストメソッドの左側に緑色のマークが付与されます。(失敗すると赤色のマーク)

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

複数テストクラスがある場合は以下を実行することでテストを実行可能です。

$ ./gradlew test

Fastlaneなどを使用して自動化することも可能になります。

ログを仕込む

テストケースないから何かしらの値を出力したい場合はprintlnを使用することで値を出力することができます。

@Test
fun testAdd() {
    val result = manager.add(2, 3)
    println("result $result")
    assertEquals(5, result)
}

出力した値は「Run」タブから確認することができます。

【Kotlin/Android】JUnitの使い方!単体テストの実装とテスト駆動開発(TDD)

アノテーション

JUnit4で使用できるアノテーションの種類と使い方をまとめていきます。

@Test

テストケースを定義するために付与するアノテーション。これを付与しないとテストコードとして認識されない。また例外を検証するには@Test(expected = Exception.class)とする。

@Test
fun testDivide() {
    val result = manager.divide(10, 2)
    assertEquals(5, result)
}

/** 例外を発生させるテスト */
@Test(expected = IllegalArgumentException::class)
fun testDivideByZero() {
    manager.divide(10, 0)
}

@Before

各テストの実行前に共通の初期化処理を実装するために付与するアノテーション。各テストケース実行前に呼ばれるので変数の初期化などに活用する。

import org.junit.Before

class ExampleTest {
    private var x: Int = 0

    @Before
    fun setUp() { x = 5 }
}

@After

各テストの実行後に共通のクリーンアップ処理を実装するために付与するアノテーション。各テストケース実行後に呼ばれるので変数の初期化などに活用する。

import org.junit.After

class ExampleTest {
    @After
    fun tearDown() {
        println("Test complete.")
    }
}

@BeforeClass

テストクラス全体で1回だけ実行する初期化処理を実装するために付与するアノテーション。対象メソッドは静的メソッド(@JvmStaticを指定してcompanion object)にする必要がある。

import org.junit.BeforeClass

class ExampleTest {
    companion object {
        @BeforeClass
        @JvmStatic
        fun setUpClass() {
            println("Test class setup.")
        }
    }
}

@AfterClass

テストクラス全体で1回だけ実行するクリーンアップ処理を実装するために付与するアノテーション。対象メソッドは静的メソッド(@JvmStaticを指定してcompanion object)にする必要がある。

import org.junit.AfterClass

class ExampleTest {
    companion object {
        @AfterClass
        @JvmStatic
        fun tearDownClass() {
            println("Test class complete.")
        }
    }
}

@Ignore("理由")

指定したテストをスキップするために付与するアノテーション。

import org.junit.Ignore
import org.junit.Test

class ExampleTest {
    @Ignore("このテストは対象外とする")
    @Test
    fun testExample() { }
}

アサーションメソッドの種類

検証するためのアサーションメソッドもassertEqualsだけでなく色々用意されています。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index