はじめに
どうも、だいぶお久しぶりです。
フリーダイヤルの電話をあしらうために本気出す〜オレオを添えて〜
にて、ロストした分の実装を24日中にあげると言ったな…あれはウソだ!!
(純粋に忘れてました、ごめんなさい)
今さら(?)Kotlin で Android アプリ開発のお仕事がやってきました、やったー!
なんですけど、JUnit でのロジックテスト時に
Java でできてたことが Kotlin でできなかったので、とりあえず記事にしてみました。
なので、今回は解決策を提示しているわけではありません。
なんかご意見もらえたらなーという希望的観測のもと書いているので、
その辺を踏まえた上で読んでいただけたらと思います><
やりたいこと
- Kotlin を使って JUnit 上でテストする
- モックなんてなかった
個人的には「モックなんざ不要(シナリオテストで巻き取ればいいじゃーん)」派。
もちろん、使わざるをえないパターンがあるのはわかってますが、
今回はそこについて論じる気はないです。
テスト対象の FizzBuzz クラスを作る
というわけで、今回のテスト対象は「FizzBuzz 問題」のロジッククラスです。
コードは、以下の通りになります。
package com.example.hellokotlin
import android.util.Log
open class FizzBuzz {
private companion object {
const val MIN_VALUE = 1
const val MAX_VALUE = 100
const val STEP = 1
}
/**
* FizzBuzz 処理開始メソッド
*/
fun start() {
var fizzBuzzString = ""
for (i in MIN_VALUE..MAX_VALUE step STEP) {
fizzBuzzString +=
if (checkFizz(i) && checkBuzz(i)) {
",FizzBuzz"
} else if (checkFizz(i)) {
",Fizz"
} else if (checkBuzz(i)) {
",Buzz"
} else {
"," + i.toString()
}
}
// カンマが最初に入ってしまっているので、それは除いて結果を表示
show(fizzBuzzString.substring(1))
}
/**
* Fizz チェック処理
*
* @param targetValue 対象の値
* @return Fizz である/Fizz でない
*/
protected fun checkFizz(targetValue: Int): Boolean {
return (targetValue % 3 == 0)
}
/**
* Buzz チェック処理
*
* @param targetValue 対象の値
* @return Buzz である/Buzz でない
*/
protected fun checkBuzz(targetValue: Int): Boolean {
return (targetValue % 5 == 0)
}
/**
* 渡された値を表示
*
* @param targetValue 対象の値
*/
private fun show(targetValue: String) {
// Logcat で見つけにくくなるので、タグは FizzBuzz で固定にしておく
Log.d("FizzBuzz", targetValue)
}
}
実際のテスト対象は checkFizz メソッドと checkBuzz メソッドです。
アクセス修飾子問題
いきなりですが、Java と Kotlin のアクセス修飾子の差は、以下の通りです。
Java | Kotlin | |
---|---|---|
public | ある | ある |
protected | ある | ある |
private | ある | ある |
無名 | ある | ない |
internal | ない | ある |
つまり、何が言いたいかというと…
Kotlin にパッケージプライベートって概念なくない?ってことです。
Java では、テスト対象を同じパッケージに置くことで、
テスト対象のメソッドにアクセスできました。
つまり、checkFizz メソッドに対しては、こんな感じに書けたはず。
@Test
public void test_checkFizz() {
// 3で割り切れる数
{
FizzBuzz fizzBuzz = new FizzBuzz();
boolean result = fizzBuzz.checkFizz(3);
assertThat("対象の値 が 3で割り切れる値 の場合は、true になること", result, is(true));
}
// 3で割り切れない数
{
FizzBuzz fizzBuzz = new FizzBuzz();
boolean result = fizzBuzz.checkFizz(1);
assertThat("対象の値 が 3で割り切れない値 の場合は、false になること", result, is(false));
}
}
が、 Kotlin では checkFizz メソッドにアクセスできないので、ビルドエラーになります…ファッ◯ン!
※ 私は Java も好きですが、Kotlin も大好きです。
え?つまりこれって…テスト対象のメソッドを public にしないといけないってこと!?
なにそのオブジェクト志向として(?)あるまじきコーディング…。
考えた解決策1. テストだけ Java で書く
JUnit において、Kotlin なんてなかった…いいね?
幸い、Kotlin と Java は連携力ばつぐんなので、
そこはそこで受け入れて、Java で書けばいいんじゃないかな!(いい笑顔で)
考えた解決策2. がんばってみる
「テストだけ Java で書く」
まぁね、アリだとは思うんですよ。
JUnit って、そもそも Java 用のテストフレームワークだし。
でも、こう…ね?
Kotlin で始めたなら、Kotlin で終わりたいじゃないですか?
ってことで、一生懸命考えた結果のパワープレイがこちらになります。
@Test
fun test_checkFizz() {
// 3で割り切れる数
run {
val fizzBuzz = object : FizzBuzz() {
fun test() {
val result = checkFizz(3)
assertThat("対象の値 が 3で割り切れる値 の場合は、true になること", result, `is`(true))
}
}
fizzBuzz.test()
}
// 3で割り切れない数
run {
val fizzBuzz = object : FizzBuzz() {
fun test() {
val result = checkFizz(1)
assertThat("対象の値 が 3で割り切れない値 の場合は、false になること", result, `is`(false))
}
}
fizzBuzz.test()
}
}
これさ、 もう FizzBuzz クラスのテストじゃなくね?
無名クラスのテストですよ、ホント…。
違うんだジョニィ、無名クラスのテストじゃなくて FizzBuzz クラスのテストがしたかったんだ…。
まぁ、 checkFizz メソッドのテストはできてるんだけど。
所感
どうすればよかったんだろうね!!!!!!!!!!
とりあえず、Kotlin で実装するしないに関わらず、
テストは必ず Java で書くという方向に倒そうかなと思ってます。
Kotlin で統一したいなら、そういうライブラリを探してブチ込むのが一番いいんじゃなかろうか。
…どういうのがあるのか、これから調べるけどね!
でも、Kotlin でテスト書く場合、テストコードを対象と同じパッケージに置くメリットって、
可読性(この場合は、コードの追いやすさ…とか?)しかなくなるよね…。
まとめ
- ロジックのテストしたいよー
- パッケージプライベートって偉大だったんだなーって
- おうち帰りたい
参考
特になし