2011年10月21日

Scala-テスト、テストをするんだ-

Scala-テスト、テストをするんだ-

テストを書くのって大事ですよね。ScalaのテストフレームワークはSpecsが有名っぽいので使ってみます。

Specsをゲットしてくる


とにも各にも手に入れないとね。
specsのDownloadsからゲットします。私の場合は、specs_2.8.0-1.6.5.jarをゲットしました。 パスの通っている所に配置します。

・scalaのirbでテストするなら、「c:\scala\lib」にでも入れればOK(scalaがc:\scalaに入れてあるとして)
・eclipseでやるなら、「プロジェクト」→「右クリック」→「プロパティ」→「Javaのビルド・パス」→「ライブラリー」→「JARの追加(or外部JARの追加)」で追加してやる。

サンプルをやってみる


specsにQuickStartがあったので、ほとんどまんまコピーする。

import org.specs._
object HelloWorld extends Specification {
"'hello world' has 11 characters" in {
"hello world".size must_== 11
}
"'hello world' matches 'h.* w.*'" in {
"hello world" must be matching("h.* w.*")
}
}

サンプルはclassになってるけど、Eclipseで実行するにはobjectのが都合が良いのでobjectに変更&頭文字を大文字にした以外はまんま。

「プロジェクト」→「右クリック」→「デバッグ」→「デバッグの構成」

で、メイン・クラスをHelloWorldにして起動構成を作ります。後は普通に実行したら、specsの実行出力がコンソールに表示されます。 これで、とりあえずの実行は終わり。

基本的な使い方


シンプルな基本形としては、

import org.specs._
object TestSpecs extends Specification{//TestSpecsはお好きなオブジェクト名
"意味のあるテストの塊" should{
"テスト内容の説明" in {
//==でチェックできる内容の場合:チェックした数値 must_== 期待する値
1+1 must_== 2
//文字列の正規表現チェックの場合:"文字列" must be matching("正規表現")
"hello" must be matching("^h.*o$")
}
}
}

比較記述


数値の比較演算の記述は、

"1+1は2" in {
println("hoge1")
1+1 must_== 2
}
"1+1は1じゃにゃい" in {
println("hoge1")
1+1 must_!= 1
}
"1+1は1より大きい" in {
1+1 must be_> (1)
}
"1+1は2以上" in {
1+1 must be_>= (2)
}
"1+1は3より小さい" in {
1+1 must be_< (3)
}
"1+1は2以下" in {
1+1 must be_<= (2)
}



テスト間で変数共有したいよね?


テスト間で変数が共有できると便利ですよね。便利なんです。 ただ、普通に書くと変数は共有できません。

"変数の共有テスト(失敗)" should{
var x = 0
"x=1" in{
x = 1
x must_== 1 //成功
}
"変数共有できていないのでxは0のまま" in{
x must_== 0 //成功
}
}

ひとつ目のテストで、「x=1」と代入しているので、2個目のテストは失敗しそうなものですが、変数が共有できていないので、成功します。 どうすれば変数共有ができるかと言えば

"変数の共有テスト(成功)" should{
setSequential()
shareVariables()
var x = 0
"x=1" in{
x = 1
x must_== 1 //成功
}
"shareVariables()を呼び出しているので共有できているのでxは1" in{
x must_== 1 //成功
}
}

とテスト実行前に

setSequential() //テストの実行順番を定義順にする
shareVariables() //変数を共有する

を実行します。これで、変数xを二つのテスト間で共有する事ができます。

テストのループしたいよねー


少し変数を変えてとか、複数回テストしたい場合がありますよね。あるんです。そういう時は、下記のように書くことができます。

"ループテスト1" should {
//setSequential() ループ処理で呼ぶとダメです
shareVariables()
var counter = 1
doAfter(counter += 1)
until(counter == 3)

"ループ処理1" in {
println("ループ処理1 counter="+counter)
counter must be_>(0)
}
"ループ処理2" in {
println("ループ処理2 counter="+counter)
counter must be_>(0)
}
}

1から3まで(2ループ)「ループ処理1」、「ループ処理2」を実行してくれます。 このループを下記のように書くことも可能です。

var counter = 1
val counters = afterContext(counter += 1).until(counter == 3)
"ループテスト1" ->-(counters) should {
//setSequential() ループ処理で呼ぶとダメです
//shareVariables()

"ループ処理1" in {
println("ループ処理1 counter="+counter)
counter must be_>(0)
}
"ループ処理2" in {
println("ループ処理2 counter="+counter)
counter must be_>(0)
}
}

記述的には綺麗ですが、should外部に変数を作るのがどうかなーというところ。

頼れるSpecContext


テストの前後に初期化や後処理をしたい時に頼れるのがSpecContext。

object TestSpecs extends Specification {
new SpecContext{

before(各テスト開始前に行ないたい処理を書く)
after(各テスト終了後に行ないたい処理を書く)
beforeSpec(テストを始めるまえに行ないたい処理を書く)
afterSpec(テストが全部終わった後に行ないたい処理を書く)
}
}

と各処理を登録しておけば自動でやってくれるのです。ラクチンラクチン。


参考:
DeclareSpecifications


posted by purigen at 11:00| Comment(0) | TrackBack(0) | Scala | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。