こんにちは、馬場です。
前回は、こういう暗黙的型変換ははまる、という悲しい事例をScalaQueryを例に説明しました。
ただ、同じScalaのDSLで暗黙的型変換を多用しているライブラリでも、すんなり受け入れられているものもあるのです。それが ShouldMatchersです。
ShouldMatchersとは
ShouldMatchersはScalaのUnitTestフレームワークScalaTestで提供されるtraitで、テスト内のアサーションを表現するDSLです。例えば、Stackをテストする以下のような単体テストプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import org.scalatest.FunSuite import scala.collection.mutable.Stack class ExampleSuite extends FunSuite with ShouldMatchers{ test("Stackからpopすると最後にpushした値が取得でき、Stackからは削除される"){ val stack = new Stack[Int] stack.push(1) stack.push(2) val oldSize = stack.size val result = stack.pop() result should be(2) stack should be (Stack(1)) } } |
"should" という単語を使っている文で、
- popして取得した値 result は2のはず
- pop後のStack は 1 がひとつだけ入っているはず
ということを確認しています。単体テストクラスにShouldMatchers traitをミックスインすることにより、プログラム中でテストの期待値を英語に近い文で表現することができます。わかりやすいですね。
受け入れられた理由
このShouldMatchersですが、DSL部分を見ただけではどのクラスのどのメソッドが動いているのか全然わからない、そのわからなさ具合はScalaQuery以上です。ScalaDocを見ても暗黙的型変換が多用されていることがわかります。それでも、わかりにくいとはまるメンバもいませんでしたし、特別に説明することもなくプロジェクト内に浸透していったと感じました。どうしてこのような違いがでたのでしょうか。
ひとつは、やはりドキュメントが充実しているということだと思います。ShouldMatchersのScalaDocをみれば、提供されるすべてのassertion文をサンプルコードとともに確認することができます。さらに、DSLとしての文法が英語に近く直感的である、というポイントも大きいでしょう。この2つのことにより、「テストでこういう確認をしたい」と思ったとき「なんとなくこんな感じかな」と書いたコードでも動くことが多く、それで動かなくてもドキュメントを見れば絶対にわかりました。その間どのクラスのどのメソッドが、、と内部構造を意識することは全くありません。
もう1点小さなことですが、traitをmixinするタイプのDSLであったことは、結構よかったと思っています。ScalaQueryは必要なクラスをimportして利用するタイプのDSLなのですが、不用意にIDEの「import の整理」機能を実行し必要なimport文が消えてしまいDSLが突然動かなくなり泣いていた人が少なからずいました。traitタイプのShouldMatchersは、mixinする「だけ」ですべての機能が使えるので、利用しやすいライブラリだったと思います。
結論
ShouldMatchersをみると暗黙的型変換を多用しても利用しやすいものもある、ということはわかりました。しかし「ドキュメントさえしっかり書いたら使ってOK」ということではなさそうです。結局、暗黙的型変換を利用して「わかりやすい!開発効率あがる!!」と感じるか、「黒魔術!!」と感じるかは、やっぱりチームごとに異なるのかなと思っています。そして、そのチームの境界線は徹底的なコード共有、レビュー、ペアプロなどで探っていくしかないのかな、と感じました。こういうところで悩むのも多機能なScalaを利用する醍醐味ですね。引き続き考えていきたいと思います。