社内の勉強会でなんかしゃべれーってことになり。なんとなくSpockがどっかで気になってたので、SpockどころかGroovyすら触ったことほぼないですけど、ドキュメントをサラーっと読んで面白いなーと思うところをSpock Web Consoleで動かしつつ紹介してみたりしました!復習がてらもういちど読んでみまする。
Spock Web Console
SpockBasics
https://code.google.com/p/spock/wiki/SpockBasics
Example
全体はこんな感じかな。読みやすいね。
class MyFirstSpecification extends Specification { def "pushing an element on the stack"() { setup: def stack = new Stack() def elem = "push me" when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem } }
これをWebConsoleで実行してみるテスト!
MyFirstSpecification - pushing an element on the stack
失敗するようにしてみると。
stack.size() == 2
こうなる
MyFirstSpecification - pushing an element on the stack FAILED Condition not satisfied: stack.size() == 2 | | | | 1 false [push me] at MyFirstSpecification.pushing an element on the stack(Script1.groovy:12)
キャーステキー!!!
てことで説明を読んでみる。
Blocks
6つのブロックがある。
- setup:
- when:
- then:
- expected:
- cleanup:
- where:
setup:
準備するとこ
setup: def stack = new Stack() def elem = "push me"
when: と then:
when: と then: はセットで使われる。when: でテストしたいことを実行して、then: でチェック。
then: のところはassertion使わなくても、bool値になってればいいみたいね。読み易いや。
when: stack.push(elem) then: !stack.empty stack.size() == 1 stack.peek() == elem
例外の場合
thrown()メソッドが使えると。
when: stack.pop() then: thrown(EmptyStackException) stack.empty
例外の内容をチェックしたい場合はこんなふうにできる。
when: stack.pop() then: def e = thrown(EmptyStackException) e.cause == null
もちょっと違う書き方だとこんな感じ。こっちのが型がはっきりしてるからIDEにやさしいよって。それと、前から読む感じになるので、読みやすいよって。僕もこっちのがなんとなく好きだな。
when: stack.pop() then: EmptyStackException e = thrown() e.cause == null
例外が投げられなかったことのチェックはこんな感じ
def "HashMap accepts null key"() { setup: def map = new HashMap() when: map.put(null, "elem") then: notThrown(NullPointerException) }
モックを使ったテストなんかも書けるよーって。詳細は http://docs.spockframework.org/en/latest/interaction_based_testing.html かな。
def "events are published to all subscribers"() { def subscriber1 = Mock(Subscriber) def subscriber2 = Mock(Subscriber) def publisher = new Publisher() publisher.add(subscriber1) publisher.add(subscriber2) when: publisher.fire("event") then: 1 * subscriber1.receive("event") 1 * subscriber2.receive("event") }
publisher.fire("event")を呼び出したら、1回だけsubscriber1.receive("event")が実行されるよってことみたいね。
mockとかspyとかありそうなので、また今度チェックしてみよっと。
expected:
実行してチェックを1行で書いたほうが自然なときはwhen:とthen:じゃなくて、expected:が使えるよーって。例えばこういう場合は
when: def x = Math.max(1, 2) then: x == 2
expected:使って一行で書くほうが分かり良いよね
expect: Math.max(1, 2) == 2
cleanup:
後処理すね
setup: def file = new File("/some/path") file.createNewFile() // ... cleanup: file.delete()
例外が投げられてもここは実行されるとのこと。いいなー。
where:
パラメタライズドテストで使うっぽいすね。これいいすね。さらっと http://docs.spockframework.org/en/latest/data_driven_testing.html を読んでみた。
例えばこういうテストをしたいとき
class MathSpec extends Specification { def "maximum of two numbers"() { expect: // exercise math method for a few different inputs Math.max(1, 3) == 3 Math.max(7, 4) == 7 Math.max(0, 0) == 0 } }
こう書けるす。
class Math extends Specification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 4 0 | 0 | 0 } }
読みやすくて素敵すなー。さらに、メソッドの引数は where:で分かるとのことで書かなくてもよいと。それと入力値と期待値の間はもう一個パイプを増やした方が分かり良いやろーって。こうなる
class DataDriven extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 3 | 5 || 5 7 | 0 || 7 0 | 0 || 0 } }
ちょっとのことだけど。そゆとこ気を遣ってるのいいねー。
パラメタライズドテストの実行結果
実行結果がどういう風に出力されるかってのを見たいので、失敗するようにしてみよう。
class DataDriven extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 3 | 5 || 5 7 | 0 || 5 0 | 0 || 0 } }
んで、WebConsoleで実行!
DataDriven - maximum of two numbers FAILED Condition not satisfied: Math.max(a, b) == c | | | | | 7 7 0 | 5 false at DataDriven.maximum of two numbers(Script1.groovy:4)
ふむふむ。ところで @Unroll というアノテーションをつけるとパラメータ毎に展開してくれるっぽい。やってみよう。
class DataDriven extends Specification { @Unroll def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 3 | 5 || 5 7 | 0 || 5 0 | 0 || 0 } }
結果はこうなった。なる。
DataDriven - maximum of two numbers[0] - maximum of two numbers[1] FAILED Condition not satisfied: Math.max(a, b) == c | | | | | 7 7 0 | 5 false at DataDriven.maximum of two numbers(Script1.groovy:5) - maximum of two numbers[2]
でも、パラメータの値をメソッド名に展開してくれる仕組みがなかったっけ?と思ってたら次に書いてあったわ。
class DataDriven extends Specification { @Unroll def "maximum of #a and #b is #c"() { expect: Math.max(a, b) == c where: a | b || c 3 | 5 || 5 7 | 0 || 5 0 | 0 || 0 } }
実行ー!
DataDriven - maximum of 3 and 5 is 5 - maximum of 7 and 0 is 5 FAILED Condition not satisfied: Math.max(a, b) == c | | | | | 7 7 0 | 5 false at DataDriven.maximum of #a and #b is #c(Script1.groovy:5) - maximum of 0 and 0 is 0
そうそう、これこれ。分かりやすいね。
Specifications as Documentation
ドキュメントとしての仕様か。
setup: "空の銀行口座を用意して" // ... when: "100円振り込むと" // ... then: "残高が100円になる" // ...