テストコードとプロダクトコードを異なる言語で書く

テストコードとプロダクトコードで、それぞれ同一ランタイム上で動作する別の言語を採用した場合のTDD(というかテストコード)についての私なりの解釈です。
割と固めの文体ですが、まぁ気にしないでください。

いくつか間違ったことを書いているかもしれないので、「ここ違うよ!」「こうなのでは?」などありましたら随時突っ込みお願いします。

同一基盤上で動作する言語

現在、JVMCLIなどの基盤上で動作する言語は数多く存在します。JVM上で動作する言語としてはJavaScala、Groovy、JRubyなどがあります。CLI上で動作する言語としてはC#VB、F#などが有名です。基盤上で動作する言語で実装されたライブラリやコード内のクラス、モジュールは、同一基盤上の別の言語でも同じ解釈実行がなされます。また、同一基盤上の言語であれば、コンパイル時に生成されるバイトコードの命令セットは共通しているため、ライブラリなどを実装した言語とは異なる言語からも同じ動作呼び出しが可能です*1

このことは、テストコードにも当てはまります。つまり、ある言語で実装したプロダクトコードを、同一基盤上の異なる言語で記述したテストコードを使ってテストすることが可能です。

異なる言語でテストする場合の利点と欠点

同一基盤上の異なる言語でテストコードを記述することには、利点と欠点があります。なお、ここでの利点や欠点は、プロダクトコードに採用した言語よりもテストコードに採用した言語のほうが高機能な場合を前提に記述しています。

まず利点から見ていきましょう。

  1. テストコードが記述しやすい
  2. テスティングフレームワークの選択肢が増える
  3. テストコードやテスト結果の可読性が向上する場合がある

テストコードが記述しやすい理由としては、一部の紋切り型のコードを記述しなくてもよくなったり、シンタックスシュガーによって簡単に記述可能なコードが存在するためです。紋切り型のコードを記述する必要がないことは、余計なコードを書く必要がなくなった分、テストコードの記述速度向上につながります。また、シンタックスシュガーを利用することで、記述速度はさらに向上します。

テスティングフレームワークの選択肢が増える理由は、テストコードで採用した言語で実装されているテスティングフレームワークが選択肢に含まれるためです。これは逆の場合、つまりテストコードで採用した言語がプロダクトコードで採用した言語よりも機能が少ない場合は成り立ちません。構文解釈の違いや言語し様の違いによっては、テスティングフレームワーク内で採用されている内部DSLを記述できない(コンパイルエラーになる)場合があるためです。

テストコードやテスト結果の可読性が向上する場合があるのは、テスティングフレームワークによって提供される内部DSLや言語機能として組み込まれたシンタックスシュガーによって可読性が向上したり、比較検証結果の出力が言語機能自体で読みやすい形で提供されているためです。

では次に、欠点を見ていきましょう。

  1. 複数の言語を習得する必要がある
  2. 言語によって文法が異なるので、記述するコードによって頭を切り替える必要がある
  3. テストコードに記述されたコードが、テスト対象メソッドやクラスの利用の例示となるわけではない
  4. 言語間をまたいだ場合、ツールの一部機能が利用できない場合がある

複数言語を習得する必要があることは、当たり前のことではありますがコストがかかります。テストコードには複雑なロジックを記述すべきではないので必要になる機能は限定されますが、基本的な文法やリテラル、シンタックスシュガーを学ぶことに学習コストがかかるのは避けられません。

言語によって文法が異なるので、記述するコードによって頭を切り替える必要があることは、その言語に慣れていない場合は記述速度の低下を招きます。先にあげた記述速度向上の利点を打ち消すレベルで、記述速度が低下する可能性も否定はできません。

テストコードに記述されたコードが、テスト対象メソッドやクラスの利用の例示となるわけではない理由は、文法が異なっていたり、プロダクトコードで採用した言語にはない機能を利用してテストコードを記述している可能性があるためです。似たような形式で記述することは可能ですが、それも限界はありますし、別の言語を使用する際の利点を失ってしまいかねません。

IDEやツールによっては、言語間をまたいだ場合には一部機能が利用できない場合があります。これは、単一の言語であれば自動で行えていた作業を、手作業でしなければならなくなることを意味します。毎回手作業で作業しなければならなくなってしまえば、ミスが増えたり作業効率が低下してしまってもおかしくありません。ただし、ツールは日々改善されていくので、バージョンアップに伴い言語間をまたいでも機能が問題なく利用可能になる場合もあります。

このように、テストコードとプロダクトコードで異なる言語を採用した場合、利点と欠点が存在すると考えています。

テストコードに別の言語を採用する条件

では、どのような条件を満たせば、テストコードに別の言語を採用しても問題ないのでしょうか?
私は以下のように考えています。

  1. テストコードに採用する言語をある程度習熟している
  2. 複数言語での開発になれる
  3. 別の言語で書いたテストコードは使い捨てにして*2、後でテストコード書き直す
  4. チーム間で、テストコードに別の言語を使うことを了承している
  5. プロジェクトで利用しているツールが複数の言語をまたいでもある程度問題なく機能する

採用したい言語をある程度習熟していることは前提条件となります。ある程度は知っておかなければ、意図しない挙動に悩まされて余計にコストがかかってしまうためです。

テストコードを使い捨てるのはコスト問題から難しい可能性もありますが、Developer TestとSoftware Testを別個に書いているのであれば(そんな会社があるのか知りませんが)、Developer Testは書きやすい、駆動しやすい言語で書けばいいのではないでしょうか。

チーム間で了承するのは、チーム開発では当たり前のことですよね?

ツール側に問題があって駆動感が妨げられる場合は、Developer Testに影響がでてくるので別言語の使用は控えたほうがいいのではないでしょうか。

*1:ただし、言語が採用しているパラダイムや言語仕様によっては、他言語で実装したクラスやモジュールが利用しづらい場合があります。

*2:DVCSを採用しているのであれば、ローカル側ではコミットしておき、リモート側にはpushしないという手段をとることも可能です