テストメソッドをもっと雑に使ってもいいんじゃないか
これは kstm アドベントカレンダーの22日目の記事です。新鮮なネタは無いので過去のツイートを元にポエムを書きます。
なお、設計能力の無い学生が何らかのフレームワークを使用してWebアプリケーションを構築する前提で話します。
TL;DR
最初からテストを書こうと思うんじゃなくて、ちょっとこのメソッドの挙動を確かめたいから、適当なテストクラス作ってとりま実行してみよー、でいいのかもしれない。デバッグが終わったらテストクラスを消してもいいし、ちゃんと整理できそうだったらそれがテストケースになるし。
— (TAT)j (@tatarhy) 2016年9月10日
Intellij IDEA + JUnit だと Test アノテーションを付けたところに矢印が出るのでそこクリックすればテストメソッド実行できて楽だし、適切なIDEとテスティングフレームワークを使えば障壁はひくいのかも
— (TAT)j (@tatarhy) 2016年9月10日
なぜこんなことを考えたのか
xUnit がどこでも同じように使えそうだった
今使っているフレームワークがフィクスチャをロードするコマンドなどを提供していなかったため別の手段が必要だったがちょうど使えたのがテストだった、というのがきっかけで、テストを単なるタスクランナーとみなして良いのではと思い始めました。もちろん、パッケージマネージャやアプリケーションフレームワークがタスクを実行するための機構を提供していたりするのですが、そのインターフェースはそれぞれ異なり、覚えるのが面倒です。一方 xUnit 系フレームワークは多くの言語に存在し、そのインターフェースは似通っています。そして多くのアプリケーションフレームワークがサポートしており、そしてフレームワークによってはテストメソッドからDIコンテナが呼べるようになるなどの拡張が提供されていたりします。よって定常的に実行するタスクならばコマンドに移植する必要があるかと思いますが、自分しか使わなかったり、暫定的にしか使わなかったりするタスクについてはテストメソッドで十分なのではないかと思います。
開発の足場としての Controller の代替
ところでアプリケーションに新機能を追加する、という場合、みなさんはどのように開発しているのでしょうか。私は直接 Controller に Action 生やして、ページ上に print デバッグしながら機能を作っていました。唯一知っていたのがその方法だったからです。
PHPフレームワークなんかだとWebページをリロードするだけで変更が反映されたり、ダンプを見やすく整形して表示してくれる関数があったりするのであまり負担ではないのですが、JavaなんかだとWebアプリケーションをリスタートするのが案外時間がかかったりするため、テストメソッドでデバッグしたほうがトライアンドエラーがしやすかったりします。
また、Controllerに直接書く場合、Controllerに密結合なコードを書きがちでかつ、動いてしまうことに甘んじてリファクタリングせずにそのままプロダクションに投入しがちになる気がします。本来は新機能におけるビジネスロジックはその責務を負う別のクラスに切り出されるべきで、テストに書く場合、そのままではアプリケーションからは利用できないので移植することが必要となるため、これがリファクタリングの契機になったらいいなと考えています。
ユニットテストなんだかんだで書けない問題
TDDではテストファーストが原則となっていますが、これには何をテストするか明確になるまで設計する必要があります。しかし開発経験が浅い場合、要件が曖昧、フレームワークの知識が浅い、設計のノウハウが無いなどの要因により、事前の設計ができず、コードを書いてみて、試行錯誤しながら徐々にコードが固まってくるというプロセスになる気がします。したがってTDDの話を見知ってテストファーストをしようとしても何をテストしたらよいかわからず挫折するように思います。
一方でテストラストの場合、テスト対象が試行錯誤の末ある程度まとまったときにテストを書くため、開発過程の手助けにはなっていません。また、設計能力が無いので設計が良くなかったり仕様が曖昧だったりして、開発中に仕様が頻繁に変わる可能性があります。この場合テストコードも変更しなければなりません。さらに、ユニットテストではアプリケーションがエンドツーエンドで正しく動くことは保証できないため、銀の弾丸にはなりません。こういった状況により、テストによる恩恵よりも負担が大きくなり、テストを書くメリットが享受できずテストが書けないように思います。
print デバッグとテストは似ている?
Webページ上で print デバッグしながら開発する場合、明確でないにせよ、なにかぼんやりと期待した動作というものが心の中にあって、それがトライアンドエラーを繰り返すことで明確化していき、最後 print デバッグした出力が期待したとおりの出力になっていたときに機能が実装できた状態になる、というプロセスになる気がします。これは大枠としてはTDDのレッドグリーンリファクタリングのサイクルと変わらないのではと思いました。うまくいけば、 print 文を assert 文に替えるだけでもしかしたらテストになるかもしれない。
私は何もわからなくてもとりあえず実践する機会を増やす、というのは向上のきっかけになると考えています。とりあえず雑にトライアンドエラーする場所としてテストメソッドで作業することでテストを書くためのきっかけになるのでは、という期待があります。
おわりに
書いてみたけど全然まとまりませんでした。ありがちな問題という体で書いていますが、自分が陥ってい(た|る)問題であり、どう改善していくのが良いかというのは自分でもよくわかりません。テストを書いて幸せになりたい(なれるとは言っていない)。