TDD in Action
ペアでTDDしつづけるイベントです。
一緒にペア組んでいただいた方にめちゃイロイロ教えていただきました。ちょうラッキーというヤツです!
# 今日のブツをgithubにupしてみました。
https://github.com/zuisener/tddact/tree/master/src/tddact
簡単なまとめ
- Javaを選択。会場はRuby, Groovy, Python, C#, などの順でした。Javaの人気のなさがやばい。
- 環境は自分のMacbook Air。ただしVirtualBox上のWindows7。キーバインドとキーボード配列がややこしくてすいません…
- お題は自動販売機の課題
- Eclipse 3.7 + Quick JUnit Plugin. Quick JUnitは2年くらい前から使いたかったけど今回やっと使えてよかった。超便利。
- 午前ラウンド終了後、ライブTDDでGroovyチームがSpockというBDDフレームワークを使っていて、じゃあそれをやってみますか的な流れに。
- 午後はずっとSpockの調査+実装に(あれ?)
Quick JUnit
会場ついてから「そうそう、Quick JUnit使おう」と思って急遽入れました。
テストとプロダクションコードを一発で切り替えたりできます。
TDDだとテストとプロダクションコードを行ったり来たりするので便利です。
- Ctrl + 9: テスティングペアを切り替える
- Ctrl + 0: テスト実行
(最初ショートカット忘れてて困ったけどペアの方に教えてもらった)
このテスティングペアの切替機能はクラス名の命名で決まります。デフォルトで<クラス名>と<クラス名>Testを切り替えます。(このルールは設定で変えられる)
ただし今回はWindows on Macの変態環境だったためかわかりませんが、Ctrl + 0が動かず毎回マウス使ってメニューから選択していました(´・ω・`)
あ、ちなみに最初無名パッケージで開発していたのですが、パッケージがないと切り替えできないようです。適当なパッケージに移動したら使えるようになりました。
JUnit4
そこそこテストが増えると見づらくなってきたのでテストをまとめることに。
JUnit4はほとんど使ったことがなかったのですがテストスイートを分けずにstaticなネストクラスごとにまとめることができるようです。
- @RunWith(Enclosed.class) をテストクラスにつけると、そのクラスのstaticなネストクラスごとにテストが実行される
実行がどうの、というより、これをやるとEclipseエディタ上でネストクラス内にテストメソッドをまとめられます。するとエディタ上ではネストクラスを折りたたみ表示できるのでとても見やすい。
JUnit4は@Test @Before @Afterくらいしか押さえてなかったけれど@RunWithのオプションは便利なようです。
他にも@Theoriesと@Datapointsとか。
いま調べてみるとJUnit4のアノテーションではパラメタライズドテストもサポートされているようです。
でも今回パラメタライズドテストは後述する別の方法で試しました。
Spock
ライブTDDでGroovyのテストフレームワークSpockが面白そうだったのでそれを使ってJavaコードをテストすることに。
いままでJava/JUnitで作ったテストをまずGroovyで実行しました。
- GroovyプラグインであるgreclipseをインストールしてEclipse再起動
- インストールする際は以下が必須。
- Extra Groovy Compilers
- Groovy-Eclipse(必須)
- Spockのライブラリをダウンロードし、ビルドパスの構成から外部Jarを追加してプロジェクトに突っ込む。
- (場所がわかりづらいのでパスを直リンク: http://repo1.maven.org/maven2/org/spockframework/spock-core/0.6-groovy-1.8/spock-core-0.6-groovy-1.8.jar ただしどうせ古くなるのでSpockのGet Startから探すことをオススメする)
- ビルドパスの設定時についでにGroovyのライブラリも追加する。
- テストコードの拡張子を.javaから.groovyに変更する
- groovyプラグインを入れるとプロジェクトエクスプローラ上のコンテキストメニューに拡張子をgroovyに変えるだけのメニューが追加されていますw
- これだけだと動かない。greclipseが新しすぎてgroovy2.0を使おうとするけれどSpockが1.8までしか対応していないので以下のエラーがでてしまう。
Groovy:Unexpected problem with AST transform: The Spock compiler plugin cannot execute because Spock 0.6.0-groovy-1.8 is not compatible with Groovy 2.0.0. For more information, see http://versioninfo.spockframework.org VendingMachineTest.groovy /VendingMachine/src/tddact 行 0 Java 問題
- Groovy1.8で動かすようにする
org.codehaus.groovy,2.0.0.xx-20120703-1400-e37-RELEASE,plugins/org.codehaus.groovy_2.0.0.xx-20120703-1400-e37-RELEASE/,4,false
- 設定が終わるとgroovyのコードでテストが書けるし、動くようになる。
- もしかするとビルドパスの構成で追加したGroovyライブラリを一旦除去して追加し直す必要があるかも
@Test public void 十円玉() { v.insert(10); assert 10 == v.getAvailableTotalAmount() // この行がassertEqualsメソッドをgroovyの記法に一部変えてみたところ } @Test public void 五十円玉() { v.insert(50); assertEquals(50, v.getAvailableTotalAmount()); }
- うごいたので今度はテストメソッドまるごとSpockっぽく書きなおして見ることに。
def "投入したお金と合計金額"() { expect: v.insert(amount) v.getAvailableTotalAmount() == totalAmount where: amount | totalAmount 10 | 10 50 | 50 100 | 100 1000 | 1000 }
このwhere句の書き方で表形式っぽく見える部分がすごいわかりやすい!(※ 等幅フォントに限る)
各行のパラメータでそれぞれexpectが実行されるようになってます。パラメタライズドテストわかりやすい。
ただしJUnitのテストメソッド1つだけをdef形式?で書いて動かしてもこの部分はスルーされてしまいます。
というのもSpockで動かすにはテストクラスがSpecificationクラスを継承している必要がある。
ということで新規にgroovyテストクラスを作成し、ちょっとずつ移植。無事SpockでJavaコードがテストできました!
こーんなかんじにパラメータに可変長なものを使いたい時はリストを入れたりもできる
def "複数回お金を投入する改"() { expect: for(i in amount) { v.insert(i) } v.getAvailableTotalAmount() == totalAmount where: amount | totalAmount [10] | 10 [10, 50] | 60 [10, 50, 100] | 160 }
パラメータにオブジェクトを使いたい時はShared変数という物を使う。
ちなみに@Sharedを使うときは手動でimport文(import spock.lang.Shared)を書く。Eclipseが自動訂正してくれないので…
(というかgroovyソース全般的に補完が弱い。メソッド名補完できないとか)
単にJava/JUnitでのフィールド変数というだけな気がする...
@Shared juice1 = new Juice("コーラ" , 150, 5) @Shared Juice juice2 = new Juice("レッドブル", 260, 0) @Shared Juice juice3 = new Juice("レッドブル", 500, 10) def "ジュースの在庫情報を得られる改"() { expect: Set<Juice> juiceStock = v.getJuiceStock() juiceStock.containsAll(juiceList as Set) dummy == 1 where: juiceList | dummy [juice1] | 1 [juice2] | 1 [juice1, juice2] | 1 }
ちなみにEclipseのGroovy対応がいまいちなのか型チェック無しのバージョンでやっているからか不明だがQuick JUnitは使えないのでテスティングペアの切り替えは手動で面倒。
Spockはパラメタライズドはわかりやすくかけて便利だけどそれ以外のメリットあまりないかも…。
ただしSpock(というかGroovy?)のおかげのPower Assertの機能が便利すぎる。
@Test public void 十円玉() { v.insert(10); assert 11 == v.getAvailableTotalAmount() // わざと期待値を間違えて書いている }
Power Assertによって以下がビューに表示される
Assertion failed: assert 11 == v.getAvailableTotalAmount() | | | | | 10 | tddact.VendingMachine@1814dd0 false
※ いまいち綺麗に表示されてませんがフォント(CSS?)のせいです。Eclipseでもコンソールフォントを等幅のものにするときれいにどの変数・式がどういう内容なのかわかりやすく表示されます。
いままでワンライナーで書いてたりすると、その行がしくっていてもどの部分式が死んでるのかよくわからないのでデバッガで逐次実行して状態を確かめたりとか死にたくなる感じでしたが、それが一発でわかる!超便利!
帰り際に参加者のかたとこの辺を話していたのですが、Spock+PowerAssertで新しく使うライブラリのAPIを学習するのに自分で適当なパラメータを入れて、戻り値を予想して、それを突き合わせる、とかいう学習テストをするときに便利とのことです。おお、すばらしい。
その他
TDDは以上なのですが、ライブコーディング中に気になった他人のエディタなど。
- WebStorm: JavaScript IDE http://www.jetbrains.com/webstorm/
- Sublime: Text Editor (Mac, Win, Linux) http://www.sublimetext.com/
- あとはintelliJ IDEA(http://www.jetbrains.com/idea/) とか...
勉強になったこと
- TDDやるまえにドメイン洗い出すのは確かに。忘れがち。テストする品質も。
- 世の中には多言語をふつーに語れるすごい人がいっぱいいる
- Specs2 DIS
知らないEclipseショートカット
- aEで補完するとassertEqualsが補完
- エディタ上でAltを押しながら上下矢印すると選択行が移動
おやつなど。
ということで名前はしってたけど使ってなかったものや新しいものを知れて非常に良かった2日間でした。
主催者のみなさまナイスな機会を与えていただきありがとうございます。
すばらしい会場提供していただいたニフティさん、日本オラクルさんありがとうございました。