テスト駆動開発のすすめ
はじめに
自分は少し前、テスト駆動開発(Test Driven Development、TDD)を使った小規模案件に携わることになりました。携わったと言っても自分ではなく、若手メンバーがテスト駆動開発を行い、自分がサポートをしたのですが、出来上がったプログラムの品質が良く、とても良い体験でした。
また、自分はScrum Alliance®認定スクラムデベロッパー(CSD®)でもあるのですが、そのCSDの研修でも講師のDavid Bernsteinさんがテスト駆動開発の有用性を説いていました。
自分はこのテスト駆動開発を知ることによってエンジニアとしてのレベルが大きくアップしたと考えていますが、リファクタリングなどと比べるとまだまだテスト駆動開発はアジャイル開発においても広く知られた手法とは言えません。
この記事では、そのテスト駆動開発についてどのような特徴があるか、なぜテスト駆動開発を学ぶことがエンジニアにとって重要なのかを自分なりの言葉で説明していきます。
テスト駆動開発とは
アジャイル開発にはさまざまな方法論がありますが、スクラムと並んで有名な方法論に、XP(エクストリーム・プログラミング)があります。XPは現代のアジャイル開発、特に技術的な側面に大きな影響を与えています。
XPから広まったプラクティスとしては「ペアプログラミング」「継続的インテグレーション」が有名ですが、そのXPのプラクティスの1つに「テストファーストプログラミング」があります。
この「テストファーストプログラミング」を開発手法としてより洗練させたのがテスト駆動開発です。XPの提唱者であるKent Beck氏が書籍「Test Driven Development: By Example」で紹介し有名になりました。
このテスト駆動開発では、次の3つのプロセス「レッド・グリーン・リファクタリング」を繰り返します。
- 動作しないテストを書く
- テストが動作するための最低限のコードを書く
- リファクタリングする
(もう一つ漏れているのでは?と気づいた人もいるかもしれません。そのもう一つについては後で出てきます)
自動テスト、テストファーストとの違い
テスト駆動開発と混同しやすい概念が2つあります。「自動テスト」と「テストファースト」です。
「自動テスト」は実装コードのテストを手動で行うのではなく、テストをコードとして記載してテストを自動的に行う手法です。継続的インテグレーション(CI)と組み合わせて実施されることが多く、一度作って終わりではなく継続的に発展させていくアジャイル開発ではもはやなくてはならない手法です。
「テストファースト」はその名の通り、テストを先に書く手法です。「テスト駆動開発」は先に述べた通り、3つのプロセス「レッド・グリーン・リファクタリング」を繰り返します。
この3つの概念をテストコード、実装コードの作成タイミングの違いという観点でおおまかに図として表すと、次のようになります。

テストファーストでバグを修正する
テスト駆動開発を理解するために、まずテストファーストの有用性を説明します。テストファーストはテストコードを先に書いてから実装コードを書く手法ですが、このテストファーストはバグを修正する際に頻繁に使います。
バグを修正する際にはまず再現条件を見つけます。再現条件がないバグの修正は困難だからです。そして再現条件を見つければ、その再現条件を「失敗するテストコード」として記述することが可能です。再現条件を「失敗するテストコード」として記述することで問題の理解が深まり、かつこの失敗するテストコードが成功すれば、バグが修正されたことが分かるという2つの意味があります。
次に先ほど書いた「失敗するテストコード」が通るように、すなわち「成功するテストコード」になるようにプログラムを修正します。これでバグ修正は完了ですが最後に、場当たり的な直し方になっていないか修正したコードを見直し、必要ならリファクタリングを行います。
もちろんコードを修正したとき、リファクタリングしたときは今回追加したテストコードを含めた自動テストを行い、修正が間違っていないかを検証します。このプロセスを箇条書きにすると次のようになります。
- バグの再現条件を見つけ出し、それを表現する最小のテストコードを書く
- テストが通るようにコードを修正し、自動テストを実行する
- コードをリファクタリングし、自動テストを実行する
このプロセスを図にすると次のようになります。

このテストファーストは頻繁に使うため、ぜひ身につけてほしい手法です。
TDDは小さな課題解決の繰り返し
先ほどはテストファーストの有用性を説明しました。これを応用することで、テスト駆動開発(TDD)の有用性を説明します。
先ほど記載したテストファーストによるバグ修正プロセスは、「バグの再現条件」を「課題」に変えることで、バグ修正以外の課題にも応用できます。
- 課題を見つけ出し、それを表現する最小のテストコードを書く
- テストが通るようにコードを修正し、自動テストを実行する
- コードをリファクタリングし、自動テストを実行する
この3つのプロセスを繰り返すと次の図のようになります。ただしプログラムは少しずつ変わっているので、「プログラム'(ダッシュ)」のように表現しています。

ただしテスト駆動開発においてもう一つ重要なものがあります。TODOリストです(最初に「もう一つ漏れているのでは?」と書いたのはこのTODOリストです)。このTODOリストは「書くべきテスト」のこともあれば「プログラムに必要な機能」、あるいは「リファクタリングが必要なコード」かもしれません。このTODOリストに気がついたら追加していきます。
TODOリストから課題を取り出し、それをテストコードとして書き、テストが通るように修正し、リファクタリングを行う。この小さなプロセスを繰り返すことで開発手法として洗練させたのがテスト駆動開発、そう考えています。
また、このイテレーティブ(反復型)かつインクリメンタル(漸増的)のプロセスには、設計(内部設計)を見直す機会が組み込まれています。これにより、プログラムが大きくなるにつれて設計も洗練されていきます。
このTODOリストと設計の見直しを図に追加するとこのような感じです(最初の∅は空のプログラムを指します)。

TDDを採用するかどうかの見極め
先ほど述べたように、テスト駆動開発(TDD)は「設計」のための手法です。そのため例えば、仕様の使いやすさが重要なライブラリ作成にはテスト駆動開発が向いています。コマンドラインのプログラム(CLI)を作るのにも向いています。
その一方でフレームワーク(Webアプリケーションフレームワーク)、特にフルスタックフレームワークを使う場合、フレームワークのお約束に従うのが重要です。なのでこの場合は設計の自由度が低く、テスト駆動開発を採用するのは難しいです。テスト駆動開発を採用しづらいと感じるケースのほとんどはこのような場合です。
ただしフルスタックフレームワークを使う場合も、複雑なビジネスロジックをクラスに抜き出したり、ユーティリティ関数を作成するときなど、テスト駆動開発が有効な場面はあります。
このときに必要なのは、テストが容易な関数やクラスを抜き出すためのスキルです。そしてこのスキルを身につけるためにテスト駆動開発の経験は非常に役立ちます。そのため、自分は実際にテスト駆動開発を採用するかどうかは場合によるが、テスト駆動開発を学ぶことはとても重要だと考えています。
テスト駆動開発の学び方
テスト駆動開発を学ぶときにはまずテストコードに慣れてからという人が多いと思いますが、自分は早いうちにテスト駆動開発に触れた方が良いのではと考えています。なぜなら、テスト駆動開発を体験することで、モックをたくさん使うのではなく、そもそもテストコードを書きやすいコードを書くという習慣を身につけられるからです。
テスト駆動開発を学ぶ際には自分はまず、提唱者であるKent Beck氏の「Test Driven Development: By Example」の翻訳「テスト駆動開発」を読んで、本に書かれている通りにそのまま書き写すこと(写経)をお勧めしています。実際に手を動かしてプログラムを書くことで、テスト駆動開発のパワーを体感できるからです。
おわりに
テスト駆動開発は英語でTest Driven Developmentと呼びますが、自分はこの意味は「テストが開発を促進する」だと考えています。英語で書くと Testing drives development ですが、このワードで検索すると、そこそこ出てきます。
この「テストが開発を促進する」をインターネットでよく使われる表現をするとこんな感じです。
あ…ありのまま 今 起こった事を話すぜ!
『おれはテストを書いていたと思ったらいつのまにか設計が終わっていた』
な… 何を言ってるのか わからねーと思うが
おれも何をされたのかわからなかった… 頭がどうにかなりそうだった…
自分がテスト駆動開発を学んだのは2018年、エンジニアとして15年以上経ってからでした。正直もっと早めに身につけておけば良かったと残念に思っています。この記事を読んでいる人は他の人に差をつけられるチャンスです。早いうちに学ぶことで良い習慣をつけられる、テスト駆動開発を学んでみてはどうでしょうか。
この記事を書いた人

Advent Calendar!
Advent Calendar 2024