2015.11.7
0からはじめるPHP#52【開発基礎知識#2-テストとテスト駆動開発(TDD)-】
今回は
テストというものに触れてみたいと思います。
テストって何だろうって話なんですが、まぁめちゃくちゃ噛み砕いて言えば動作テストです。
こんなことを言うと
「え、ろんぐさん動作テストもしないで開発してたの?」とか言われそうですけど、それができたら神なので。僕には無理です。(笑)
いまいちピンとこないんですが、テストの仕方にも色々ありまして、とりあえず動かして変数をダンプして
(変数の中身を画面上に表示する)っていうのは、一番手っ取り早くて簡単なテスト方法ですし、これは誰しもが通る道です。
ただ、もう一歩進んだ段階のテストは、もう少し違うことをするのです。
僕もテストの考え方に関しては全く初心者なので、調べながら書いているのですが、
考え方は色んなページで解説されています。
パラダイムが理解できても頭で実行できるかどうかはまた別問題なんですけど、とりあえず興味ある方は色々調べてみてください。
まぁ、当たり前のことですが
「正常系」と
「異常系」にざっくり分けて考え、それぞれでテストするワケです。
正常なデータが送られてきた時はもちろん、
変なデータが送られてきた時のエラー処理もきちんと確認しなくてはなりません。
ただ別にこんなのは普通確認するはずですけどね。でも普段から意識しておくことでテストの漏れも防ぐこともできるかも知れませんね。
ちなみに、ろんぐさんはどちらかというと
性格が悪いが故に悪意あるデータを送りたくなるタイプなので異常系テストはよくします(何)
それぞれについて色んなチャートを考えて、入力した値から出力される値が想定をクリアしたものなのかどうかを確認するってのがテストです。簡単ですね。
さて、例えばこの掲示板のようなものですと、実際に色々フォームに打ち込んで出てきたデータを確認して・・・・・・みたいなことをやっているかというと
やってないんですよ。
ソフトウェア開発の現場では
このようなテストもプログラム化します。
つまり、実行したらテストになるようなプログラムを別に作りたい、ってワケです。
まぁ、ぶっちゃけ掲示板レベルのショボいコードだとそんなことやんなくてもアナログでなんとかなるんですが、経験にならないのでテストもできたら作ってみたいなと。
その方法自体は後で学習することになるのですが
Laravelにテストの機能があります。
PHPフレームワークのテストなので、Webに関することは大体できそうな感じがします。フォームへの値の埋め込みなんかも自動でやってくれます。さすがにクッキーは無理なんですかね。まぁその辺は工夫次第でなんとかなるでしょう。まぁなんなくてもクッキーぐらいならアナログ確認できると信じてます。(笑)
テストの技法自体には現段階で触れないことにしてますが、タイトルにあるもう一つの話、
テスト駆動開発の話もついでにしようかと思います。
ついでにする、といいながら実はこれ
さっき知った話です。
実は
前回はXP(エクストリーム・プログラミング)の話をするといったんですが、その中の一要素がこの「テスト駆動開発」というものなんですが、僕みたいな個人開発の場合、そんな大規模な知識は要らないんで具体的な開発手法さえ知っとけばいいと思うので、とりあえずこのテスト駆動開発、つまり
TDD(test-driven development)に焦点を当てましょう。
といっても、テクニックの話なんで用語の説明をすれば事足りますので、そんな難しい話じゃないです。
ものすごいざっくり言うと
先にテストを作っちゃえってことです。
信じられませんよね。
「無い」ものを動かすプログラムを先に書けって言ってるんですよコレ。
どんな風にこのTDDを行っていくかは
wikipediaの説明を見たら大体わかります。
- 失敗するテストを書く
- できる限り早く、テストがパスするような最小限のコード本体を書く
- コードの重複を除去する(リファクタリング)
- まず、現時点で分かっている範囲でテストの必要性がある項目をリストとして列挙する。このリストは適時、テストの必要性がわかった時点でその項目を追加していく。
- このリストから1つ選ぶ。これは、実装できそうなものでしかしトリビアルでないものを選ぶ(テスト自体の記述が容易でも、Fake It(後述)でしかコード本体を記述できなさそうなものは後回しにする)。実装できそうなものが無い場合は、列挙した項目の粒度が大きすぎることを意味する。
その場合、それを実装するための前提にできるような、より小さい粒度の項目を作りリストに加える。
- 選択した項目についてテストを記述する。このテストは、現在の実装を用いると失敗するようなものを選ぶ。
- コンパイルに必要な最小限のコード(例えば、まだ存在しないクラス・メソッドを利用するテストを書いたなら、そのクラス・メソッドの宣言)の追加後、実際にテストを実行し失敗することを確認する。期せずしてテストがパスした場合は、意図しないことが起こっていることに注意する。テストが失敗するまでは、コード本体は触らない。
- できる限り早くテストの失敗を解消するようにコード本体を記述する。この段階では、テストをパスさせるためにどんなことをしても良い(定数を返す、コピー&ペースト、コードの重複等)。
具体的には3つの方法が挙げられる。
- 実装が自明な場合(1分程度で書ける場合)はそれを記述する(Obvious Implementation)。
- テストに要求される値そのものをハードコーディングする(Fake It;仮実装)。
- Fake Itの後に次のリファクタリングの段階へ進めないような漠然としている場合は、さらに別のデータを用いたテストを追加し、その2つのテストの共通点を見出して助けとする(Triangulate;三角測量)。
- テストがパスすることを保持しつつ、コード本体やそれとテストの間の明示的・暗黙的重複を取り除く(リファクタリング)。
通常、リファクタリングとはコードの意味を変えずに再構築することを言うが、ここでは、「コードの意味」はテストが通ることを言う。リファクタリングで取り除く対象となる重複は、形式的なものだけではなく意味的なものも含む。例えば、Fake Itでハードコーディングしたものはおおよそ、実際にはどこからか得られるはずのパラメータの値を、それとは別に値を用いて算出したものであり、これを重複と見なす。重複を取り除くことでロジックが抽出される。
- リストから実装した項目を削除する。作業中にテストの必要性が判明した他の項目に、実質的に振り変わるかもしれない。
コピペしたのでめっちゃ見難いんですが、噛み砕くと
どんな手を使ってでもテストをクリアしさえすれば良いってことです。
例えばデータベースから値を取り出して・・・・・・とかいうステップは
最初から無視してテキトーな定数を返しとけっていう無茶苦茶なことをします。そんなもん後で実装すりゃいいんだよ。
とりあえず場当たり的に開発し、後からリファクタリングによりダブりを取り除け、ってことです。
はい、皆さん。このような開発手法をどう思うでしょうか。
面白くね?(笑)
説明を見れば言わんとすることはわかるかと思いますが、僕のような個人開発レベルの小さいプロダクトだとだと
ちまちま設計考えるより作ったほうが早いってことが往々にしてあると思うんですよ。
これが企業での開発とかになるときちんと設計から考えたほうが良いのかも知れませんが、「とりあえず作ってみて、ダブりがなくなるように設計しながら作り進める」ってのも面白い方法だと思うんです。
この背景には
最初から完璧な設計ができる方が少ないという事実があります。
どんだけ頭を捻って考えた設計も、蓋を開けてみれば微妙だったりもしますし、それ以上に
後の修正が利かないのが一番の欠点なんですよ。
想像できると思うんですが
作ってる内にもっといい機能を思いついたとか
作ってみたら使い勝手が悪い場所を発見してしまったとか。そういうのは結構あるんですよね。
で、そこで柔軟に対応できるような開発手法が、このようなアジャイル的手法であり、テスト駆動開発もそのうちの一つです。
ってことで、まだいまいちピンと来てないんですが
これを取り入れてみるのも面白いかなということで、このテスト駆動開発で掲示板開発を回していくのもアリかなーと思ったりします。
具体的にどういうテストケースを考えればいいのか全く分かんないんですが、まぁそこは作りながら・・・・・・。
ということで、次回からは実際にテストを作ってみようかなと思います。