daisuzz.log

Kotlinで名前付き引数とデフォルト引数を使ってテストデータを生成する

今回は、Kotlinでテスト用のdata classを生成する方法を書いていきます。

概要

builderパターンを使ってテストデータを生成できるが、Kotlinでは名前付き引数とデフォルト引数を使ってさらに簡単にテストデータを生成できる

テストデータを生成する

テスト用のデータを生成する方法としては、test data builder pattern が有名です。 test data builder patternについては、下記サイトが参考になります。 blog.nextscape.net 詳しくは、省略しますがテストデータの生成にbuilderパターンを用いることで無駄な記述を減らすことができるパターンです。

ここからは、test data builder patternを使った方法と、名前付き引数とデフォルト引数を使ってテストデータを生成する方法を紹介していきます。

まず前提として、下のコードのようなPersonクラスとPhoneNumberクラスがあると想定してください。

data class Person(val name: String, val age: Int, val phoneNumber: PhoneNumber)

data class PhoneNumber(val number: String)

test data builder pattern をつかった方法でテストデータを生成するときのコードを下に示します。

class PersonBuilder {

    private var name: String = "Alice"

    private var age: Int = 20

    private var phoneNumber: PhoneNumber = PhoneNumberBuilder().build()

    fun build(): Person = Person(name, age, phoneNumber)

    fun withName(name: String): PersonBuilder {
        this.name = name
        return this
    }

    fun withAge(age: Int): PersonBuilder {
        this.age = age
        return this
    }

    fun withPhoneNumber(phoneNumber: PhoneNumber): PersonBuilder {
        this.phoneNumber = phoneNumber
        return this
    }
}

class PhoneNumberBuilder {

    private var number: String = "000-000-000"

    fun build() = PhoneNumber(number = number)

    fun withNumber(number: String): PhoneNumberBuilder {
        this.number = number
        return this
    }
}

test data builder patternでは、Builderクラスを用意しそのプロパティとして、対象テストデータクラスのプロパティと同じものを定義します。 また、定義したプロパティを使って対象テストデータクラスのインスタンスを生成するbuild()を定義し、さらに引数で渡されたプロパティの値をセットし、Builder自身を返すwithXXXX()を定義します。

実際にテストデータを生成する処理は以下のようになります。

class TestPerson{

    val alice = PersonBuilder().build()

    val bobPhoneNumber = PhoneNumberBuilder().withNumber("111-111-111").build()
    val bob = PersonBuilder().withName("Bob").withPhoneNumber(bobPhoneNumber).build()

    /* 省略*/
}

aliceのようにbuild()を呼び出しただけの場合は、PersonインスタンスのそれぞれのプロパティはPersonBuilderのプロパティのデフォルト値になります。

一方、bobのように途中でwithXXXX()をチェーンさせると、そのメソッドに対応するプロパティがセットされたPersonインスタンスを生成することができます。

このようにtest data builder patternを使うと変更したいプロパティだけ宣言すればよいので、コードの可読性を向上させることができます。 しかしデメリットとして、大量のテストデータクラスの場合、Builderクラスを大量に作らないといけなくなりコードの記述量が増えるという問題があります。

そこで、Kotlinの名前付き引数とデフォルト引数を使ってこの問題を解決していきたいと思います。 上記のコードをデフォルト引数を使って書くと以下のコードになります。

fun buildPerson(
    name: String = "Alice",
    age: Int = 20,
    phoneNumber: PhoneNumber = buildPhoneNumber()
) = Person(name, age, phoneNumber)

fun buildPhoneNumber(number: String = "000-000-000") = PhoneNumber(number)

Kotlinではメソッドに引数にデフォルト値を定義できるので、このような書き方ができます。

実際にテストデータを生成する処理は以下になります。

class TestPerson{

    val alice = buildPerson()

    val bobPhoneNumber = buildPhoneNumber(number = "111-111-111")
    val bob = buildPerson(name = "Bob", phoneNumber = bob2PhoneNumber)
}

buildPerson()の引数に何も渡さない場合、デフォルト値をもつPersonインスタンスが生成されます。 buildPerson()に名前付き引数を渡すと、その名前をもつプロパティがセットされたPersonインスタンスが生成されます。

test data builder patternと比べると、builderのコードの記述量が圧倒的に少なくなっていることがわかると思います。

まとめ

今回は、名前付き引数とデフォルト引数を使うことでより簡潔にテストデータを生成する処理を紹介していきました。 実際にかなり役立つ方法だと思うので、ぜひ利用してみてください。

参考

Test Data Builderパターン | ネスケラボ

Best Practices for Unit Testing in Kotlin