1. なぜテストコードを先に書くのか
アジャイル開発は、「テストファーストの開発」です。テストコードが先にあれば、プログラムの
仕様が明らかです。また、リファクタリングする必要がある場合、コードと元のコードとの間で振る舞いに
差異がないかを確かめられます。
このテストファーストとリファクタリングの組み合わせが、より良い技術やより良い設計
を適用するを躊躇させません。逆に、どんどんやってみようという動機を与えてくれる。
この感覚は、変更履歴が管理されているとか変更前のコードに戻せるという安心感とは違います。
1.1 条件分岐を使って書かれた文字列操作の例
テストのテスト文字列パターンと抽出結果を見ると、文字列<abc>から<>で囲まれた文字列だけを取り出す仕様であることが
分かります。もし先頭と末尾の文字が規則に従って記述されていない場合は、nullを返す
ことも分かります
テスト:文字列テストパターンと期待される抽出結果が比較されている
public void testStringExtraction() { String result; result = extractor.extract(""); assertEquals("<abc>", "abc", result); result = extractor.extract(" <abc>"); assertEquals(" <abc>", "abc", result); result = extractor.extract("<abc> "); assertEquals("<abc> ", "abc", result); result = extractor.extract("<abc!"); assertEquals("<abc!", null, result); }
元のプログラム:
条件分岐を使って、positionの値によって先頭文字(<)、文字列、終端文字(>)を判別しています。
public class StringExtraction { public String extract(String str) { StringBuffer sb = new StringBuffer(); int position = 1; int len = str.length(); char c; for (int i = 0; i < len; i ++) { switch (position) { case 1: c = str.charAt(i); if (c == '<') position ++; break; case 2: c = str.charAt(i); if (c == '>') { position ++; break; } sb.append(c); break; default: break; } } if (position == 3) return sb.toString(); return null; } }
2. どうテストを活用してリファクタリングを行うのか
このような条件分岐はState Patternを適用して、柔軟でシンプルなコードに変更可能です。
テストコードがあれば、仕様書が無くてもある程度仕様を把握すことができます。そして思い切って
将来のためにリファクタリングできるのです。
コード修正が終ったら、テストコードによりリファクタリングが正しくなされたことを再度検証します。
State Patternを適用してリファクタリングしたコード:
public class StringExtraction { public String extract(String str) { int len = str.length(); ExtractorState state = new ExtractorWaitingForStartChar(); for (int i = 0; i < len; i ++) { state = state.extract(str.charAt(i)); } return state.getResult(); } }
- State パターン:元のコードではpositionの値が先頭文字(<)、文字列、終端文字(>)に対応してインクリメントされています。 この点に注目して、positionの値を状態に対応させています。
参考文献:Agile Software Development by Robert C Martin