Scalaでテスティングフレームワークを試してみる。

ネット情報やガイド本での記述量からの推測だが、Scalaでは何となくテスティングフレームワークがにぎやかな感じがする。逆説的に、Scalaのコードはテストしにくいということかもしれない。Scala型推論などでコード量が少なくて済むというが、Scala初心者の私は、まだ可読性の恩恵を受けている実感がない。参照するAPIや表記法を完全に理解できていないためでもあるが、サンプルコードを眺めてみてもコード量の少ないことを強調している割りには、可読性や試験性(テスト容易性)が本当にいいのかと疑わしいのだ。特に、extendsやwithで大量に継承されるといやだな。作法としてのコードスタイル、テストを容易にするためのコードスタイルなどは提唱されていないのかな。デシジョンカバレッジを100%にするようなテストは書けるのか、そもそもScalaステートメントやブランチを網羅的にテストすることはできるのか。

テスティングフレームワーク

JUnitでさえ十分には使ったことがないのだが、せっかくだからScalaでテスティングフレームワークを学習しておきたい。代表的・標準的なものとしては、以下のようなものが挙げられていた。
 ・ScalaTest
 ・Specs2
 ・JUnit
 ・ScalaCheck
 ・TestNG
 ・ScalaMock
 ・Mockito
 ・Setak
 ・Jacoco
とりあえず、私も、ScalaTestとSpecs2を試してみた。カバレッジを見れるものも知りたかったが、まだリサーチ不足だ。Jacocoは試したがまだうまく動作してくれない。Scala開発の現場での実態はどうなのだろう。非常に小さなサンプリングだが、比較的Scala経験の長い人に伺ってみたところ、ScalaTestやSpecs2をバリバリつかっている感じではなかった。テスト駆動が浸透していないだけなのか、Scalaのテスティングフレームワークが好みにそぐわないのかは判断できない。またネット情報でも、テスト用のコードやデータを自動生成してくれるようなものもあまり見当たらない。「scala-testgenというテスト自動生成ツールをつくりました」という情報があった程度だ。Eclipse上でtest用のコードのテンプレートくらいは自動生成してくれてもいいのだが、Scalaの開発では重いEclipseは敬遠されているということだろうか。IDEならIntelliJ IDEAを選択した方がいいのかな。
テスティングフレームワークは、ユニットテスト用のものと受け入れテスト用のものの両方を提供するものが多い。ユニット(=コンポーネントScalaならClassやObject)の外部仕様をテストするものを「受け入れテストスタイル」といっているようだ。ユニット内部の制御構造などをチェックするものが「ユニットテストスタイル」だ。テスト駆動で開発するなら、ユニットのインタフェースが決まったら(実際にはこのタイミングや判定がはっきりしない)、まず受け入れテストスタイルでテストコードを書けということだろう。しかし、ほとんどのScalaのガイド本は、ユニットテストスタイル、受け入れテストスタイルの順に記述されていて、テスト駆動を推進する感じがないのだ。実態はぜいぜいユニットテストスタイルに留まっているということかもしれない。
受け入れテストスタイルは、ユースケースのシナリオに合わせて記述できそうだ。クラス単体の外部仕様をテストするというより、プレゼンテーション層とやり取りするコントロール層のテストに使えそうだ。ユースケースのシナリオ記述に沿った機能テストに使える。ScalaTestでは before,afterといったメソッドも用意されていて、シナリオの事前条件、事後条件を表現できる。BDD(ふるまい駆動開発)と言っているが、オブジェクト指向であるならもっとユースケースのシナリオ駆動と言い切っていいのではないか。
また、ガイド本にあった受け入れテストスタイルのサンプルが情けない。Hello Worldのサンプルアプリに受け入れテストスタイルを適用するには無理があるだろう。せめてHello Worldの文字列を生成して返すコントロール層とそれを表示するプレゼンテーション層に分離して、コントロール層の機能テストに受け入れテストスタイルを適用するというなら許せるかな。Hello Worldのサンプルが受け入れテストスタイルを誤った方向に誘いそうだ。

ScalaTest

一応、ガイド本をもとに、ScalaTestとSpecs2を試してみた。ScalaTestにはFunSuiteやFunSpecのトレイトが用意されている。これらは matchers を内包しないので ShouldMatchers や MustMatchers をミックスインするスタイル(extends FubSuite with MustMatchers など)で利用する。一方、Specs2の Specification は matchers も内包しているので継承するだけ(extends Specification のみ)で matchers が使える。
scalatestの公式サイト(http://www.scalatest.org/download)からScala 2.10用のjarを「Download ScalaTest 1.9.2 Jar」のリンクからダウンロードし、Eclipseのビルドパスに追加しておく。build.sbtを編集して、以下の記述を追加し、cmdからsbtを起動する。追加の際は、Scala式のセパレータである空行を挟んでおくこと。


libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "1.9.2" % "test"
)
まずは、FunSuiteを継承したクラスを作成し、都度assertを使って記述してみる。基本はJUnitと変わらない感じだ。コードは割愛する。

Specs2

Specs2のSpecificationは、その名のとおり、テストを書くのではなく、仕様をコードで表現するんだという意気込みが伝わってくる。Specs2を誤解して捉えていたかもしれない。しかし、私にはSpecificationの表記法についていけそうにない。Scalaでもない、別のスクリプト言語といった感じだ。Specs2のSpecificationは後回しにしたい。
Specs2で注意する点をネット上で見つけた。Specs2では複数のテストケースの実行はデフォルトで並行に行われる。before,afterのメソッドが適用される順序は保証されないかも知れない。明示的にsequential()を呼び出して、並行処理しないようにする必要があるそうだ。
ScalaTestもSpecs2も、libraryDependenciesをbuild.sbtに確実に記述し、必要なjarがビルドパスに追加されていれば、テスティングフレームワークは動作するようだ。手順やサンプルコードは、ガイド本に従えばよい。また後日、詳細に調査してみます。