はじめに
Googleが出している TestParameterInjector を使うと、すこぶる簡単にParameterizedテスト(パラメータ化テスト)を書くことができたので、サンプルコードなどを載せつつ説明したいと思います。
TestParameterInjectorに関して
詳しくは https://opensource.googleblog.com/2021/03/introducing-testparameterinjector.html のブログ記事を参考いただければと思いますが、簡単にまとめると…
- Googleが2021年3月に公開したJUnit4 Test Runner
- Googleのコードベースで新しいテストのために最も使用されているフレームワーク
TestParameterInjector is now the most used framework for new tests in the Google codebase:
という感じです。
使用する場合は
testImplementation 'com.google.testparameterinjector:test-parameter-injector:1.3'
と dependencies
に追加すればOKです。( 1.3
は執筆時のバージョン)
サンプルコード
サンプルコードを交えつつ、 TestParameterInjector
を使うとどうなるのかを説明します。
Piyo
テスト対象のコードとして Piyo
クラスを用意しました。
say
メソッドがあり、引数の value
により
- 2で割り切れるときは「piyopiyo」
- それ以外のときは「piyo」
を返却します。
class Piyo {
fun say(value: Int): String {
return when {
value % 2 == 0 -> "piyopiyo"
else -> "piyo"
}
}
}
PiyoTest
Piyo
に対して TestParameterInjector
を使わないで 素朴なテストコードを記述すると以下のようになるでしょうか。
- 2のときに「piyopiyo」であること
- 1, 3のときに「piyo」であること
- 0のときは「piyopiyo」であること
見ていただけるとわかりますが、パラメータの値( value
)とAssertion時の値が違う以外はほぼ重複したコードになってしまっています。
class PiyoTest {
@Test
fun piyo_say_piyopiyo_when_2() {
val value = 2 // ここと
val piyo = Piyo()
val result = piyo.say(value)
Assert.assertEquals("piyopiyo", result) // ここが違うだけ
}
@Test
fun piyo_say_piyo_when_1() {
val value = 1
val piyo = Piyo()
val result = piyo.say(value)
Assert.assertEquals("piyo", result)
}
@Test
fun piyo_say_piyo_when_3() {
val value = 3
val piyo = Piyo()
val result = piyo.say(value)
Assert.assertEquals("piyo", result)
}
@Test
fun piyo_say_piyopiyo_when_0() {
val value = 0
val piyo = Piyo()
val result = piyo.say(value)
Assert.assertEquals("piyopiyo", result)
}
}
TestParameterInjectorを使ってみる
Piyo
に対して TestParameterInjector
を使って パラメータ化テストを作成してみると以下のようになります。
@RunWith(TestParameterInjector::class) // ← ①
class PiyoTest2 {
// ↓ ②
enum class PiyoTestCase(val value: Int, val expected: String) {
CASE0(0, "piyopiyo"),
CASE1(1, "piyo"),
CASE2(2, "piyopiyo"),
CASE3(3, "piyo"),
}
// ↓ ③
@Test
fun piyo_say(@TestParameter piyoTestCase: PiyoTestCase) {
val value = piyoTestCase.value // valueを使う
val piyo = Piyo()
val result = piyo.say(value)
Assert.assertEquals(piyoTestCase.expected, result) // expectedを使う
}
}
-
@RunWith
でTestParameterInjector
を指定する -
PiyoTestCase
で引数(value
)と期待値(expected
)の組み合わせを定義する - テストの引数に
@TestParameter
を指定したPiyoTestCase
を指定する
こうしてやると
テストコード piyo_say
の引数が
-
CASE0
の場合 -
CASE1
の場合 -
CASE2
の場合 -
CASE3
の場合
と4回呼ばれることになります。
↓実行した結果もこの通り、テストメソッドは1つなのに、4つのテスト結果が出力されます。
TestParameterInjector
の適用前・後でテストコードがかなりスッキリしたことがおわかりになったかと思います。
その他の方法
上記のサンプルコードではテストケースをenumで定義し、テストメソッドの引数に渡す方法でしたが、
-
Boolean
やInt
を使う -
@TestParameter
アノテーションを付けてフィールドに定義する
なども可能です。
フィールドに定義した場合は、テストコード全体でパラメータ化されて実行されることになります。
@RunWith(TestParameterInjector::class)
class MyTest {
@TestParameter
var isDryRun = false
@TestParameter("1", "2", "3")
var i = 0
@Test
fun test1(@TestParameter enableFlag: Boolean) {
// 12通りの組み合わせで呼ばれる
// test1[isDryRun=false,i=1,false]
// test1[isDryRun=false,i=1,true]
// test1[isDryRun=false,i=2,false]
// test1[isDryRun=false,i=2,true]
// test1[isDryRun=false,i=3,false]
// test1[isDryRun=false,i=3,true]
// test1[isDryRun=true,i=1,false]
// test1[isDryRun=true,i=1,true]
// test1[isDryRun=true,i=2,false]
// test1[isDryRun=true,i=2,true]
// test1[isDryRun=true,i=3,false]
// test1[isDryRun=true,i=3,true]
}
@Test
fun test2() {
// 6通りの組み合わせで呼ばれる
// test2[isDryRun=false,i=1]
// test2[isDryRun=false,i=2]
// test2[isDryRun=false,i=3]
// test2[isDryRun=true,i=1]
// test2[isDryRun=true,i=2]
// test2[isDryRun=true,i=3]
}
}
また Advanced usage として動的な @TestParameter
の生成などもできるようなので、興味のある方は見てみても良いかもしれません。
https://github.com/google/TestParameterInjector#advanced-usage
終わりに
TestParameterInjector
の簡単な説明とサンプルコードをご紹介しました。
テストコードが驚くほどスッキリするので、使えるところでは積極的に使って、楽しくテストコードを書いていきたいところです。