お茶漬けびより

"あなたに教わったことを、噛んでいるのですよ" 五等分の花嫁 7巻 「最後の試験が五月の場合」より

超簡易な xUnit を Kotlin 入門しながら書く

f:id:pickles-ochazuke:20181202154024j:plain

Pixel 3 で写真撮るの楽しいです。久々の更新です(毎回言ってる気がする)。
Kotlin Advent Calendar 2018 5日目の記事です。

qiita.com

テスト駆動開発の本に書かれているプログラムを Kotlin で書いてみました。自分は Kotlin を触って日が浅いのですが、入門本を一冊読んでみたので何か書いてみようかと思ったのと、最近 「テスト駆動開発」という本を読んだので試しに xUnit 書いてみるかと。

クラスは、TestCaseTest, TestCase, WasRun, TestResult, TestSuite を作り、関数として getAttr 関数を作りました(機能的には getMethod という感じですが)。

Python では、オブジェクトの変数やメソッドを取り出す getAttr 関数があるのですが、Kotlin にはなさそうだったので、メソッドを取り出す関数を作りました。といってもただ Java のリフレクションを使っただけですが。以下のような感じです。

import java.lang.reflect.Method

fun getAttr(obj: Any, method_name: String): Method {
  return obj.javaClass.getMethod(method_name)
}

メソッドや関数の宣言の仕方はあとで書いてあるので飛ばしますが、上記のようにインポートすれば、Java のライブラリを使うことが出来ます。もっと上手いやり方がある気がするのですが本題じゃないので今回は上記のようにしました。

まず、基本クラスになる TestCase ですが、以下のようになります。

open class TestCase(var name: String) {
  open fun setUp() {
  }
  open fun tearDown() {
  }
  
  fun run(result: TestResult) {
    result.testStarted()
    setUp()
    try {
      val method = getAttr(this, this.name)
      method.invoke(this)
    }
    catch(e: Exception) {
      result.testFailed()
    }
    tearDown()
  } 
}

open class ですが、Kotlin はデフォルトで継承が禁止されています。なので、継承させるために open を指定あげます。class は察しの通りクラスの定義を意味します。

次の TestCase(var name: String) は、TestCase がクラスの名前で、括弧内はクラスの持つ変数の宣言とプライマリコンストラクタの仮引数を表します。このように書くことで引数の値が宣言されている変数内に格納されます。ちなみに name は、テストしたいメソッド名を持ちます。

理解しやすいように書くと以下と同じ意味になります。

// open class TestCase constructor(name: String) でも同じ意味
open class TestCase(name: String) {
  var name: String

  init {
    this.name = name
  }
}

これでコンストラクタと変数の初期化の仕方が分かりました。次は、メソッドです。 open fun setUp(), open fun tearDown() ですが、open でオーバーライドを許可しているメソッドになります。

fun run() メソッドですが、仮引数の TestResultクラスは、テスト結果をカウントするクラスです。 run メソッドは、テストを実行するメソッドです。初めにテストの回数を増やし、テスト前に必要な初期化処理を行います(実際は継承先でオーバーライドされている処理が走ります)。その後、例外処理の try ブロック内でメソッドを実行します。失敗したときにプログラムが終了しないように catch 内で テストが失敗したことをカウントします。

Kotlin のアクセス修飾子ですが、デフォルトでは public です。Java とは異なるので要注意です。

こちらが参考になると思います。 qiita.com

次に WasRun クラスを見ていきます。

class WasRun(name: String): TestCase(name) {
  var log: String

  init {
    log = ""
  }

  override fun setUp(): Unit {
    log = "setUp"
  }
  fun testMethod(): Unit {
    log = "${log} testMethod"
  }
  override fun tearDown() {
    log = "${log} tearDown"
  }
  fun testBrokenMethod() {
    throw Exception()
  }
}

WasRun クラスは、テスト対象となるクラスです。ここでは、TestCase のテストを行いたいので、TestCase を継承しています。class WasRun(name: String): TestCase(name) で使われているコロン(:)は、それのあとに書かれているクラスを継承することを意味しています。そして、コンストラクタに渡す引数として、WasRunクラスの変数を渡しています。 残りのメソッドは、オーバーライドをしていたり、テストに必要なメソッドを追加しています。testBrokenMethod() 内の throw Exception() ですが、見ての通りエラーを発生させています。

また、"${log} testMethod" のように文字列テンプレートを書くことが出来ます。

最後に、TestSuite クラスを見ていきます。

class TestSuite(val tests: MutableList<TestCase> = arrayListOf<TestCase>()) {

  var result: TestResult
  
  init {
    result = TestResult()
  }

  fun add(test: TestCase) {
    tests.add(test)
  }

  fun run(result: TestResult) {
    tests.forEach{
      it.run(result)
    }
  }
}

TestSuite クラスは、テストをまとめて行うためのクラスです。class TestSuite(val tests: MutableList<TestCase> = arrayListOf<TestCase>()) は、変数 tests に引数がなければデフォルト引数として arrayListOf<TestCase>() を入れるようにしています。 Kotlin の List についてはまだ詳しくないのですが、 readOnly と mutable なリストの二種類があります。簡単に紹介すると readOnly なリストは、中身を変えることができないリストで、 mutable なリストは、中身を変えることができるリストです。ただし、readOnly は以下のように書くことで中身を変えることが出来ます。

var list = mutableListOf(10, 20, 30)
var list2: List<Int> = list
list[0] = 33

リストについては以下が参考になると思います。

qiita.com

qiita.com

TestSuite では、様々なテストを tests にまとめておきます。そして run メソッドによってリストの中身をすべて実行していきます。

fun run(result: TestResult) {
  tests.forEach{
    it.run(result)
  }
}

簡単ですが、以上で Kotlin で作った xUnit の紹介を終わります。

Kotlin を使ってみた感想

書いてて楽しい言語だなと思いましたね。セミコロン必要ないですし、デフォルトで public になっていたり、継承がコロンだけで済んだり、また、継承もデフォルトで禁止されているのは良いなと思いました。null を許容しないのもいいですね。

Kotlin は、短く書けるけど何でもかんでも短くはしないというか、書きやすいと読みやすいのバランスが良いという感じがしました。

ぜひ仕事で使ってみたいので、修得してサーバやスマフォ開発で使いたいですね。

参考

入門するために読んだ本です。これは何か言語を知っていれば短時間で Kotlin にどんな機能があるのか把握できるので、とてもよかったです(何か言語を使ったことある前提)。

速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズ

速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズ

テスト駆動開発の本です。改訂版ですが、とても翻訳のクオリティが高く、言語も今に合わせて翻訳者が書き直してくれています。しかも付録として歴史的な話も載っています。さらに値段も安いです。すごいです…。

テスト駆動開発

テスト駆動開発

以下もおすすめらしいです。

Kotlinイン・アクション

Kotlinイン・アクション

記事内のコード

github.com

おれ、今の仕事納めたら Kotlin 勉強するんだ……