#本記事の内容
1.TDD(テスト駆動開発)とは
2.TDDの進め方
2-1. 目標の考え方
2-2. テストの書き方
2-3. テストの失敗
2-4. 目的のコードの書き方
2-5. リファクタリングの実施
3.参考文献
#1.テスト駆動開発(TDD)とは
テスト駆動開発とは「Test Driven Development」の略で、簡単にいうと綺麗なコードを素早く書くための手法。
「Clean Code that works」: 動作する綺麗なコード
を実現するためにテストを実行しながらコードを完成させていく。
#2.TDDの進め方
下記の図のような3つの基本サイクルを回しながらTDDを進める
ここで、
RED : テストの失敗
Green : テストの成功
Refactor : リファクタリングの実施
を表す。
テストを実行した時、テスト失敗時はRED、成功時はGreenで示されるのでその色として覚える。
**基本サイクル(1 ~ 6)**を繰り返し実行し、コーディングしていく。
- 作成したいコードの目標を考える
- 目標を示すテストを書く
- 2で書いたコードを実行して失敗させる(RED)
- 目的のコードを書く(テストを成功させるためのコード)
- 2で書いたコードを成功させる(Green)
- テストは成功するのが前提条件でリファクタリングを行う(Refactor)
###2-1.目標の考え方
取り組むべき目標を考えてコードを作成するためにそれぞれの目標を細分化し、To-Doリストを作成する
目標の決め手は、大きく2つある。
- 最も重要なものを最初に実施
- テスト容易性が高いものから実施(テストしやすいもの)
TDDでは、後者のテスト容易性が高いものから実施する。
目的に合わせてテスト用意性が高いものから順にTo-Doリストの作成をする。
以下にTo-Doリスト考え方の一例を記載
例)
ボタンをタップしてタップした回数を表示する
ただし、タップした回数が3の倍数なら「3の倍数」と表示する
タップした回数が100の倍数であれば「Great」と表示する。
ここで、表示するというものはテスト容易性が低いものであるので優先度を下げる。
(表示というものはテスト実行時でも確認することができ、コードを書いたとしても旨味が少ないから)
また、ボタンをタップするという動作は、重要度は高い(アプリの場合)が容易性が低いため優先度を下げる。
テスト容易性が低いものを例文から省く。
また、「ただし」という接続詞を省き可読性を高める
Fase1)
テスト容易性:高
ボタンをタップしてタップした回数を表示する
ただし、タップした回数が3の倍数なら「3の倍数」と表示する
タップした回数が100の倍数であれば「Great」と表示する
テスト容易性:低
・表示する
・ボタンのタップ
次に、タップした回数というものを言い換えてみる。
実施したいことはタップした時の「数字」を文字列として(UIに)返すということなので、Fase1)を言い換えると、
Fase2)
テスト容易性:高
数字を文字列に変換する
数字が3の倍数なら「3の倍数」に変換する
数字が100の倍数なら「Great」に変換する
テスト容易性:低
・表示する
・ボタンのタップ
Fase2)において倍数はコードの容易性を低くしてしまうため、これは親の要素として子の要素にコーディングしやすい例を書く。
Fase2)の内容をコードを書きやすい内容に変換し、例文とTo-Doリストを作成すると、
Fase3)
テスト容易性:高
数字を文字列に変換する
数字が3 の倍数なら「3の倍数」に変換する
数字が100 の倍数なら「Great」に変換する
テスト容易性:低
・表示する
・ボタンのタップ
To-Doリスト
テスト容易性:高
[ ] 数字を文字列に変換する
-- [ ] 1が入力された時、文字列の1に変換する
-- [ ] 2が入力された時、文字列の1に変換する
[ ] 3の倍数が入力された時、「3の倍数」に変換する
-- [ ] 3が入力された時、「3の倍数」に変換する
[ ] 100の倍数が入力された時、「Great」に変換する
-- [ ] 100が入力された時、「Great」に変換する
テスト容易性:低
[ ] 表示する
[ ] ボタンのタップ
このような流れで目標を定め、テストコードを書きやすくする。
ここで数字を文字列に変換するテストを2回実施するのは初めのテストなので保険をかけてN増しするため(三角測量)
###2-2. テストの書き方
テストコードの基本は、準備、実行、検証のコードをわかりやすく書くこと。
また、テスト関数の関数名はできる限り母国語でわかりやすく。(Grobalで開発する場合を除き)
2-1)で作ったTo-Doリストの上位部分からテストコードの作成に取り掛かる。
###2-3. テストの失敗(RED)
ここでは、わざとテストを失敗させる。
例えば、初回テストの関数内の検証内容は、
//準備
//実行
//検証
expect(1,actual)//actualは実際出力された値
だけを実行し、わざと失敗させる。1がactualになるはずがないので当然失敗する。
2回目以降(To-Doリストの)は、1回目に作成した内容で期待する値を変更し、実行させる。
わざと失敗させることで、デバックにかかる時間を削ることができる。
なので、ここでの失敗は予期している失敗となる。
###2-4. 目的コードの書き方
2-3)で書いたテストコードを変更し準備、実行、検証の3つを作成する。
また、それを実現するコードも作成する。
ここで重要なのは、スピーディーにコードを書くこと。
冒頭で記載した通り、TDDは、より早く綺麗なコードを実現するために考えられた手法です。
ここでのコードを書くは目標を達成するためのコードを素早く書き、テストをPassさせることです。
なのでTo-Doリスト1における関数からの返り値は
void convert(int number){return '1';}
でOK。(仮実装)
これでテスト失敗した場合は自分の書いたテストコードに間違いがある。
このテストが成功した場合、Green のテストの成功(サイクル5)となります。
またこの段階で、
void convert(int number){return '0';}
に変更し、わざとテストを失敗させることもやっておいた方が良い。
これは2-3のテスト失敗の内容だが1発目のテストは重要となり、次回テストの基本コードとなるのでやっておいた方が良い。
###2-5.リファクタリングの実施(Refactor)
2-4で書いたスピードを求めたコードを見やすく使えるコードに変更します。
例えばこんな感じに。
void convert(int number){
return 1;
}
ここで、TDDの「動く綺麗なコード」を実現させます。
ここで重要なことは、テストを成功させた状態(Green)でということになります。
今回のケースでは簡単でテストを失敗させるリファクタリングはできませんが、
複雑になってきたときに大きなリファクタリングを実行し、テスト失敗になることはNGです。
子要素のテストコードが完了した場合、親の要素を実行します。
void convert(int number){
return number.toString();
}
これでTo-Doリスト1が終了です。
To-Doリスト
テスト容易性:高
[ x ] 数字を文字列に変換する
-- [ x ] 1が入力された時、文字列の1に変換する
-- [ x ] 2が入力された時、文字列の1に変換する
[ ] 3の倍数が入力された時、「3の倍数」に変換する
-- [ ] 3が入力された時、「3の倍数」に変換する
[ ] 100の倍数が入力された時、「Great」に変換する
-- [ ] 100が入力された時、「Great」に変換する
テスト容易性:低
[ ] 表示する
[ ] ボタンのタップ
このサイクルを繰り返し行うことで綺麗なコードをスピーディに実装することができます。
#参考文献