参考書籍は Test Driven Development By Example [ Kent Beck ]、 日本語版は テスト駆動開発入門です。
今回はテストのエッセンスについてです。 テストを書いて、実装コードを書き、機能追加していく中で需要なポイントを拾って書きました。 テスト駆動開発でテストを先に書く手順の続きになります。
Assert を先に書く
テストを書くときは Assert から書くのがいいそうです。 確かに、 ゴールから決めるのは常套手段ですね。
Assert から始めるに当たって、 まず考えるのは次の2つです。
- What is the right answer? (どうなったら正しいといえるのか)
- How am I going to check? (どうやってチェックするのか、何の値をチェックするのか)
本書に載っている例を見ると、 上の2点から始めて Assert 部分 から 存在しないクラスを使ってテストを書き始め、 最終的にテストを完成させているのがよくわかります。
テストデータ
Kent Beck によれば テストにおいて使用するデータにも鉄則があります。 わかりやすいもの、 すなわち 1 でも 2 でもよいのなら 1 を使うんだとか。
またシステムによっては 実データをテストに使うこともあると思いますが、 Kent Beck は実データは特に次のようなケースで有効であると述べています。
- 実稼働している他のシステムで出力されたデータを使うリアルタイムシステムのテストをする場合。
- 変更前のプログラムと変更後のプログラムによる出力を比較する場合。
特に難しい話ではありませんが、 一旦整理して頭に入れておきましょう。
テストの実装ルール
テストコードは一般的なプログラミングルールを逸脱することがあります。 例えばマジックナンバー。 というのも、 テストにおいては実装コードを完成させることと テストによるフィードバックを必要なだけ得られるようにするのが目的だからです。 MONEY EXAMPLE
の章でもありましたが、 まずマジックナンバーを使うことで ひとまずのテストを完成させていました。
もういちど MONEY EXAMPLE
の章で列挙されていた方法について説明されています。 Fake It
と Triangulation
がありますが、 一番最初におさえないといけないのは Fake It
です。
Fake It (‘Til You Make It)
英語版102ページでは python でテストフレームワークを作っていたときの実例が紹介されています。 テストコードが Fake からどんどん変わっていく部分が再掲されています。
Kent Beck は Fake It
のメリットとして次の2つを挙げています。 2つめのは 少し前のページにも出てきていました。
- まずテストをグリーンにすることでコードに確信がもてる。 余計な心配をしなくていい。
- わかりきったものでもテストを書いてグリーンにしておくことで、 今どの機能が足りていないかをわかりやすくする。
そこから Triangulation
の話へと続きます。 内容は MONEY EXAMPLE
で出てきた話の復習です。 復習なので割愛します。
パラメータを増やす場合
テストが完成した後で機能を追加する例として sum(int value)
から sum(int[] values)
へ書き換えるときのテストコードの書き方が載っています。 もちろん説明用に作られたメソッドなので不自然なところもありますが、 流れは一通り覚えておきたいですね。
参考書籍は Test-Driven Development By Example、 日本語版は テスト駆動開発入門です。
Money Example の次は jUnit のようなテストツールを作ってみるお話です。 30以上の言語で使われている xUnit は、 今や巨大な資産ですが、 そういったものは自分で作れるものなんでしょうか。
テストフレームワークの仕組み
読んでみると、実際にやっていることは難しくなさそうです。 肝となるのは 一連のテストの結果 (実行・成功・失敗したテスト数) をどうやって保持するかではないかと思いました。 独自にテストフレームワークを作るときに役に立ちそうです。
社内でも独自にテストフレームワークを作っている人がいます、 かなりシンプルにできるそうです。 その人によると、 PHPUnit などのテストフレームワークは いろんなファンクションや機能をつけているから複雑になっているそうです。 そうじゃないと使ってもらえないからだとも言っていましたが。
JUnit や PHPUnit は TestCase
だったかのクラスを継承してテストコードを書いていくので 今回説明されていたやり方と同じですね。 でも rspec だと describe で書き始めたりするし クラス の 定義 で始めるわけでもないので 本書とは別のアプローチかもしれません。
参考書籍は Test-Driven Development By Example、 日本語版は テスト駆動開発入門です。
機能追加の方針
Franc-ly Speaking
のところ(29ページ)からです。 Franc
を導入して複数通貨に対応させるんですね。 39ページに至るまでの流れを見ていくと次のようになります。
Dollar
を複製する。 テストコードも複製する。
- 親クラス
Money
と 継承クラス Dollar
, Franc
に分ける。
- Factory Method Pattern で
Money
クラス に共通のものを集約する。
Dollar
から Money
へ、 子クラスから親クラスへメソッドを移す手順には要注意ですね。
書かないテスト
途中、 toString()
というメソッドを、テストコードを書かずに追加しています。 この判断基準は重要なのでまとめておきます。
次の条件を満たす場合は、テストコードを書かなくてもOKとします。
- 画面上で(テストの)結果を見ようとするメソッドを追加しようとしている。
- 追加するコードはデバッグのためのコードで、仮に正常に動かなくなったとしてもダメージが小さい。
- テストが Red で終了している。 (Red のときは新しいテストコードを書きたくない。)
普遍的にまとめた記述が今のところ出てきていないので toString()
追加の際の説明をまとめたものになっています。
toString()
程度 だったらテストを書いてもよさそうですが、 もっと複雑なものに成った場合には テストを書かずに進めた方がいいんでしょうか。 これは実践あるのみですね。
その後 Money Example
の演算の実装と、 全体を俯瞰した傾向、 テストの品質について重要な記述がありますが、 ひとつひとつ紹介すると長くなるので割愛します。 でもひとつだけ気になる記述があるので紹介します。
Easy to read for programmers – Unsynchronized documentation is scarce. The tests will be more valuable if they are readable, giving an interesting second perspective on the messages hidden in the source code.
テストを書けば仕様も理解しやすくなるかもしれないと思いました。
約70ページにもわたって MONEY EXAMPLE という事例が書かれています。 あまりに多いので一気に読むのはちょっと辛いですが、テストドリブンを夢見てがんばって読んでいます。 今回は テストドリブンの序盤となる部分をyんでみました。
実装の前にテストをすべて書くのは無理
MONEY EXAMPLE のところを見ると、 Kent Beck ですら最初にすべてのコードを書かないということがわかります。 MONEY EXAMPLE で取り上げられている単純な金額の掛け算でも、コードが徐々に完成していくのにつれてテストコードも変わっていきます。 よく、最初にテストコードを書くなんて無理だからテスト駆動開発は無理だという人がいますが、テスト駆動とはそういうことではないんですね。
MONEY EXAMPLE では、 存在しないクラスを使ってテストを書き始めます。 Red どころか コンパイルすらできないテストを書くところから始めています。 必要なクラスを考えてクラスのテストから書き始めるのかと思いましたがそうではありませんでした。 最後の理想形をテストに書いていました。
テストドリブンの手順
ToDo の整理をしてからテストを始めます。 この ToDo List はコードが完成するまでついて回ります。 載っている事例では、 存在しない Dollar
クラス を使ってテストを書くところから始めています。 そして テストを通してできるクラスのメソッドは(最初は)あくまでスタブです。
MONEY EXAMPLE は次のようにして進めていました。
- テストを書く。存在しないクラスも使ってよい。
- テストをコンパイルできるようにする。
- テストを Green にする。
- リファクタを行い、重複を取り除く。
テストを Green にする方法
- 固定値を返すなどして、テストを通るようにする。 (実装コードとしては完成ではない)
- 明らかにわかるコードを実装する。
- 複数の側面からのテストを書く。 (Triangulation)
Triangulation というのは、 テストケースを追加することで固定値での返り値を返せないようにするように、複数のパターンのテストを書くことで実装コードを規定していく方法です。 たとえば 1 から n までの総和を求める関数があったとして、 Sum(1) == 1
だけだったら Sum
の返り値は 固定の 1
でもいいですが、 Sum(5) == 15
だと実装せざるを得なくなります。 Kent Beck は本当にどのようにリファクタすればよいか迷うようなときに Triangulation を使うそうです。 どうすればいいのかわかっている場合は Triangulation のためのテストコードは書かれません。
MONEY EXAMPLE の中で出てきた Value Object というのも重要だと思うのでまとめておきます。
Value Object
一種のインスタンス変数で、コンストラクタで設定された値はそれ以降変わることがありません。 これにより、ポインタを通じた意図しない値の書き換えの発生を防ぎます。 別の言葉で書くなら 副作用対策です。
Value Object の持つメソッドの戻り値は必ず新しいオブジェクトにします。 下に例を書きました。 Num
の add
メソッドの戻り値は新しいインスタンスになっています。 これにより add
の前後で Num
のインスタンスは変更することがありません。 また、 primitive value ではないので、 別途 equal
メソッドを実装しておかないとテストコードを書くときに困ることがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
class Num def initialize(amount) @amount = amount end def get_amount return @amount end def add(num) return Num.new(get_amount + num.get_amount) end def equal(num) return get_amount === num.get_amount end end |
Test-Driven Development By Example の JAVA のコードをまねて作ったのですが、 言語が ruby なので実践的には get_amount
を書かずに attr_reader :amount
と書きますね。
同僚の勧めで Test-Driven Development By Example を読むことにしました。 書いたのはあの Kent Beck です。 日本語のものもあるのですが、そちらは絶版になっているので買うなら中古です。
この本では具体例が示されていて、どのようにテストができていき どのようにコードができていくのかがよくわかります。 まだ最初のほうしか読んでいませんが、他人(Kent Beck)のテストの書き方がわかるので、自分のやり方を見直すよい機会になります。
Test Driven の基本
Preface のところに Test Driven の基本ルールが書いてあります。
- 自動テストに失敗するまで新しいコードを書かない。
- 重複は排除する。
テストで失敗するまでコードを書いてはいけません。 これに関連して よくはまる落とし穴に、「テストコードのテストコードを書く必要はないのか」というのがあります。 これについて 同僚は次のような説明をしてくれました。
自動テストがないときはどうやってテストしていましたか? (PHPの場合) var_dump などでテストをしていたと思います。 テストコードのテストコードというのは、 その var_dump と 目視 のテストを書くってことと同じことなんですよ。 テストコードそのものの正しさは開発者が担保するようにして進めるんです。
わかったようなわからないような説明かもしれませんが、要するにそんなこと言っていたらきりがないからテストコードは開発者のチェックとコードの間に入れる保険なんだと思います。 またテストコードのバグについてはじゃんじゃん直していいそうです。
そして Kent Beck はコードの書き方について重要な方針を書いています。
自分の書いたコードのテストは自分で書こう
これ重要ですね。 この本をバイブルってことにしておけば、自分で書いたコードのテストは自分で書いてくれって言えますからね。 Red / Green / Refactor の順でやろうと思ったら自分で書くしかないんですけどね。
テスト駆動・自動テストのメリット
開発現場では往々にして「自動テストなんてやって意味あるのか」「時間がかかるだけじゃないのか」という議論が勃発します。 地位の高い人がそういうことを言い出すとどうしようもなくなったりします。 モチベーションにも繋がるところなので 自動テストのメリットを再確認しましょう。 本書では social implications として4つのことが書かれているのですが、それを仕事上のメリットとして書くと次のようになります。
- 汚いコードの密度が減り、品質保証は受け身の仕事じゃなくて能動的な仕事になる。
- 自分でコードを書くことが品質保証に繋がるので、問い合わせを受けてから手を動かすようなものではなくなります。
- ドッキリするようなコードに遭遇することが減り、マネージャの見積もり精度が上がる。
- 開発者のみならずマネージャにとっても嬉しいことが起こります。
- 技術的な会話の議題が明確になり、同僚との協力作業の効率が高まる。
- 開発者の能力にもよると思いますが、書籍上ではそういうことが書かれています。
- 汚いコードの密度が減り、新機能の追加されたリリース可能なコードが毎日増えて顧客との関係が向上する。
- 毎日というのは言い過ぎかもしれませんが、バグを見落とすことが少なくなるので新規機能の追加に集中できるようになります。
特に2番目は重要ポイントですね、 テストドリブンとは関係なさそうな人にもメリットがもたらされるので。 どれも理想論に見えますが、テストドリブンの目指すところは上記のメリットです。
この本を読んで今以上にテストドリブンに力を入れていこうと思いました。 中にはテストを書かない人もいるのですが、具体的な進め方も書いてある本なので 他人への説明も楽にできると思っています。
A Life Summary of an Gypsy