specs2 の Acceptance specification を使う
specs2 には二種類の書き方がある
http://etorreborre.github.com/specs2/guide/org.specs2.guide.QuickStart.html
Unit specification
「org.specs2.mutable.Specification」です。specは実装コードと交互に記述されます。
単体のクラスについて記述するための利用が想定されています。
以下のような割とおなじみの書き方です。
class XXXSpec extends Specification { "xxx" should { "yyyy" in { // テストの内容 } } }
Acceptance specification
今回の本題は「org.specs2.Specification」です。specが一つの文章のように記述され、実際のテストコードの実装は別のところに分離されます。
受け入れテストや結合テスト(シナリオテスト)での利用が想定されています。
以下のように書きます。
class XXXSpec extends Specification { def is = "xxx" ^ p^ "yyyy" ! example ^ end def example = { // テストの内容 } }
初見だとちょっと面食らうかもしれませんが、慣れます。私自身、使うまで「取っ付きづらい・・」という印象を持っていましたが、慣れてくるとこっちの方がより spec らしいように思えます。
specs2 の準備
1.7.1 は記事時点の最新安定バージョンなので、最新の情報は specs2 のサイトで確認してください。
http://etorreborre.github.com/specs2/#Downloads
libraryDependencies += "org.specs2" %% "specs2" % "1.7.1" % "test"
specification fragments
org.specs2.Specification は is という名前のメソッドが abstract になっているので、これを実装します。
「The is method lists specification fragments」とあるように spec の「fragment(断片)」を「^」で「end」までつなげます。
def is = "title" ^ p ^ "describe1" ! example1 ^ "describe2" ! example2 ^ end
この例だとfragmentは「"title"」「p」「"describe1" ! example1」「"describe2" ! example2」「end」の5つになります。
それでは、実際にシンプルなサンプルをみてみます。
package example import org.specs2._ class AcceptatnceSpec extends Specification { def is = // ------------- // spec を記述する部分 // 「is」というメソッドの実装として記述する // 「end」キーワードまで行末を「^」でつなげていく "This is a spec to check 'Hello World'" ^ p^ // p はparagraph(パラグラフ・段落)の p "The 'Hello World' string should" ^ "contain 11 characters" ! isLengthValid ^ // "description" ! body の形式で書く "start with 'Hello'" ! isStartValid ^ "end with 'World'" ! isEndValid ^ endp^ "The 'Hello World' string should" ^ "not contain numbers" ! notContainNumbers ^ p^ "The 'Hello World' string should" ^ "contain uppers" ! containUppers ^ end // ------------- // spec とは別の任意の場所にassertion の実装コードを記述する // これらを「example(some executable code returning a result)」と呼んでいる // assertion の結果 MatchResult が Result に暗黙変換され受け渡されていく val helloWorld = "Hello World" def isLengthValid = helloWorld must have size(11) def isStartValid = helloWorld must startWith("Hello") def isEndValid = helloWorld must endWith("World") def notContainNumbers = helloWorld must be matching("[^0-9]+") def containUppers = helloWorld must =~("[A-Z]") }
出力結果は以下のようになります。
[info] This is a spec to check 'Hello World' [info] [info] The 'Hello World' string should [info] + contain 11 characters [info] + start with 'Hello' [info] + end with 'World' [info] [info] [info] The 'Hello World' string should [info] + not contain numbers [info] [info] The 'Hello World' string should [info] + contain uppers [info] [info] Total for specification AcceptatnceSpec [info] Finished in 156 ms [info] 5 examples, 0 failure, 0 error
「^」と組み合わせで出てくる暗号っぽいもの
- t : タブ
- bt : バックタブ
- br : 空行
- p : br ^ bt
- endbr : end ^ br
- endp : end ^ p
インデントを揃えるのは美意識?
特にスペースの数を揃える必要はないので、IDEのフォーマッターなども問題なく使えます。
以下はIntelliJ IDEAでフォーマットした例です。美意識の違いなどでいろいろ意見がありそうですが、、こちらでも読みづらいとは感じないと思います。
package example import org.specs2._ class AcceptatnceSpec extends Specification { def is = "This is a spec to check 'Hello World'" ^ p ^ "The 'Hello World' string should" ^ "contain 11 characters" ! isLengthValid ^ "start with 'Hello'" ! isStartValid ^ "end with 'World'" ! isEndValid ^ endp ^ "The 'Hello World' string should" ^ "not contain numbers" ! notContainNumbers ^ p ^ "The 'Hello World' string should" ^ "contain uppers" ! containUppers ^ end val helloWorld = "Hello World" def isLengthValid = helloWorld must have size (11) def isStartValid = helloWorld must startWith("Hello") def isEndValid = helloWorld must endWith("World") def notContainNumbers = helloWorld must be matching ("[^0-9]+") def containUppers = helloWorld must =~("[A-Z]") }