GroovyでJUnitなテストを書くときの注意点……なんて無かった #gadvent2012
Groovy!!(挨拶)
G* Advent Calendar 2012 の13日目でございます。
- 前日: SpockでビルトインされているExtensionsとかそのへん - Yamkazu's Blog / @yamkazuさん
- 翌日:
groovy.lang.MissingPropertyException*1*2
JUnitをGroovyで使うなど
何も考えずに突っ込みどころ満載のFizzBuzzとテスト(色々足りてない)を書きます。
これをGroovyで書くとします。
- HogeGroovyTest.groovy(ほぼ HogeJavaTest.java のまま)
そのままJUnitとして動きます。Eclipse と IntelliJ IDEA では動くの確認できてます。
まんまコピペで何も嬉しくないので、Groovyらしくセミコロンを消したり、要らない括弧も消しましょう。
assertThat(sut.fizzBuzz(3), is("Fizz")); // Java assertThat sut.fizzBuzz(3), is("Fizz") // Groovy!!
素晴らしい!みんなGroovyやりたくなったね!
……んなバカな。
プロダクトコードをJavaで書いてテストをGroovyで書くアプローチはとても有効だと思ってるんですが、先に挙げたようなところで止まると微妙。いや止まらないとは思うのですが。
JUnitをそのまま使えるので、それなりのJUnit力が有れば特に問題は無いと思います。とは言っても、「何か使えなかったり、ハマる所有るんじゃないの?」とか心配する気持ちもわかります。わかりませんけどわかることにします。なので一通りやってハマりどころを書こうと思いました。
……すんなり出来すぎて*3何も出て来なかった。あえて言うなら「中途半端にGroovyにかぶれてると困るかもしれない」とか。以下はそんなメモです。
JUnitに関わるあれそれ
ごくごく普通に @RunWith とか @Rule とかも使えます。
RunWith がちょっとシンプルに書ける
ちょっとだけね。
@RunWith(Enclosed.class) // Java @RunWith(Enclosed) // Groovy
Rule はちゃんと書かないと怒られる
@Rule public TestRule watcher = new TestWatcher() { @Override protected void starting(Description description) { println description.methodName } }
折角Groovy使ってるんだから、色々と手を抜きたいと思うものです。でも上記コードの public TestRule を def とか書いたらダメーって言われる。JUnitのエラーメッセージは親切なので、こけたらわかるんですが、一応書いておきます。
JUnitが気にしてるのは @Rule がついてるフィールドが public な TestRule であることでしかないので、そこに何を入れるかは好きにして構いません。なので、上記コードはこんなふうに書くことも出来ます。誰得。
@Rule public TestRule watcher = [starting: { println it.methodName }] as TestWatcher
Thories もすんなり
@RunWith(Theories) static class TheoriesTest { HogeGroovy sut = new HogeGroovy() @DataPoints static Object[] params() { def map = [:] map << [3: "Fizz", 12: "Fizz"] map << [5: "Buzz", 10: "Buzz"] map << [15: "FizzBuzz", 30: "FizzBuzz"] } @Theory public void test(def param) { assert sut.fizzBuzz(param.key) == param.value } }
あえて言うならば @Datapoints の型は配列でなきゃヤダーと言われるところですかね。めんどくさいんで Object[] にしちゃってますが、害は無いでしょう。あまり意識してなかったのですが、ここでは map が Object[] に強制型変換食らって Entry 各々の配列になってたりします。動いてびっくりした……。
Spockを混ぜ込む
ここまで来たら、みんな大好き Spock を混ぜ込みましょう。おもむろにEnclosedランナーのクラスにネストさせるだけです。
@RunWith(Enclosed) class HogeGroovyTest { // ...略... static class HogeSpock extends Specification { def "Enclosedの中でSpockが使えるよー"() { expect: true } } }
嬉しいですね。とても嬉しいですね。こういうことが出来るのも、SpockがJUnitだから……Spockを使うときに継承する Specification が @RunWith(Sputnik.class) が付けられたクラスだったりするんです。つまりSpockは元々JUnitなので、JUnitと混ぜて自然に実行できても何ら不思議ではないんです。
Spockが使える状況下でJUnitを使うのかは正直わかりませんが、それぞれ良さがあると思うんですよ。なのでその気になればこんな風に書けるってのは、場合によっては便利。かも。JUnitなのでSpockにもRuleが使えたりします。有効な場面には今の所遭遇したことないですが。
関ジャバの裏でコード書いてたヤツら
関連エントリ
関ジャバでLambdaのセッションの後、適当にGistに書殴ってポストしたわけです。さくらばさんのセッションでも紹介されてた、色が青のものの重みの合計を出すって感じのコードですね。
で、別に Java SE 8 待たなくても「それGroovyで出来るよ?」とか言っちゃうわけです。
ほらそれっぽいでしょ!
- filter -> findAll
- map -> collect
- reduce -> inject
ってことで、Lambdaシンタックスを書く練習?にGroovyどう?とか、そんな感じで触ってみてはいかがー?ってエントリです。そしてうっかりGroovyを本採用するといいと思うんです。
その場で反応されたわけで
Forkして遊んでみた。 URL
- daiksyさん(Scala) https://gist.github.com/4180777
- kuchitamaさん(Scala, Clojure) https://gist.github.com/4180773
こう、対抗されたら返すのが礼儀ですよね!
なんか違う反応した人
2012-12-01 15:26:06 via YoruFukurou to @irof
- backpaper0さん https://gist.github.com/4180804
しれっと javap 送りつけてくるおとこのひとって……
Java SE 8 で入る Lambda の構文は新しいメソッド用と言うわけでもなく
Java SE 8 でようやく Project Lambda が入ります。何が嬉しいかってーと内部イテレーターですね。内部イテレーターってそのオブジェクトの中でぐるんぐるんするんで、外からは「この処理をしてくださいな」と渡す必要があります。それを簡単に書けるようにしたのが Project Lambda の構文。よくfilterとか新しく追加されるメソッドと一緒に紹介されますが、それだけでもありません。
現行のJava*1でも、何らかの処理をするインスタンスを渡せば出来なくはないんですが、やっぱりなんだかんだで面倒くさくございます。たとえば Collections#sort に渡す Comparator とかがそれにあたります。こういうの。
メソッド引数に new Comparator とか出てきてインタフェースを無名クラス実装して渡す。コード自体はIDEが適当に埋めてくれるので書くのはそれほど面倒ではないのですが、正直読み辛い。かといって定型の宣言や括弧を同じ行とかにねじ込んだところで、フォーマッターがサクッと戻してくれちゃいます。
クラスを別に作るって手もあるのですが、それだと遠くに書かなきゃいけないので面倒だったりします。もちろん適切な名前を付けることでわかりやすくするって効果が見込めたりしますが、そこまで汎用的な Comparator を作るなら、おそらく Comparable を実装しますよね。
これが Lambda を使うとこう書けます。
楽になりますねー。すっきりですねー。早く使いたいなー。
使いたいなら使ってみよう。Groovyで。
JDK 8u40 Early Access Releases — Project Kenai をダウンロードして直接試してもいいんですが、もし万一うっかり「もう現行のコードなんて書きたくないよハァハァ」となってしまっても、まだ Early Access Releases なので流石にプロダクトには使えないでしょう。そこでGroovyですよ。Groovyならこんな感じで似たように書けて、かつ今すぐでも使えます。
なるべくJavaを崩さないように書いています。元のJava SE 7 以前のコードもGroovyとしては通じるのですが、そこから「なるべくLambdaの書き方に近いように」って感じにするとこうなります。
無論Groovyなんで、やってることだけを純粋にやるとこうなったりします。
ですが、今回は Lambdaシンタックスを書く練習?にGroovyどう?とか、そんな感じで触ってみてはいかがー?ってエントリなので。そしてうっかりGroovyを本採用するといいと思うんです。