Chat (Lingr.com)
Informaiton
Daily
Column
- MySQL日本語の旅(5/1)
- アクセス向上秘伝(5/9)
- 一風変ったHaskellλ門(6/13)
- SICP Answer Book (5/31) 問題3.26追加
Zope Solution
Extra
アーカイブ
OSS案内所
Site Info
関連リンク
今日は、珍しく朝からコードに専念できる日。
(よーし、今日はコードをサクサク書いて、定時に帰るぞ!...)
そう思いながら、森川は内村の隣に座る。
前回のコード共有の反省もあって、自分がコードを書けるときは、ペアを必ず組もうと決意した森川。
しかし、内村とペアを組んで、すぐにある異変に気付く。
それは、内村が教材新規開始にライセンスIDを追加するために、以下の順番でコードをリファクタリングしたことがきっかけだった。
LearningEngineAccessor.java
public class LearningEngineAccessor
{
public boolean NewMaterial( String userName, String materialName, int licenseId )
{
boolean result = false;
if ( IsValidUser( userName ) && IsValidMaterial( materialName ) && IsValidLicenseId( licenseId ) )
{
...
}
}
public boolean IsValidLicenseId( int licenseId )
{
...
}
...
}
LearningEngineAccessorTester.java
import junit.framework.*;
public class LearningEngineAccessorTester extends TestCase
{
...
public void testNewMaterial()
{
assertEquals( "'test01'、'mba01'、''で起動失敗", true, m_fixture.NewMaterial( "test01", "mba01", 1 ) );
assertEquals( "'test01'、'mba02'、''で起動失敗", true, m_fixture.NewMaterial( "test01", "mba02", 1 ) );
assertEquals( "'test01'、'mba03'、''で起動失敗", true, m_fixture.NewMaterial( "test01", "mba03", 1 ) );
}
public void testIsValidLicenseId()
{
assertEquals( "", true, m_fixture.IsValidLicenseId( 1 ) );
assertEquals( "", true, m_fixture.IsValidLicenseId( 12 ) );
assertEquals( "", true, m_fixture.IsValidLicenseId( 43 ) );
}
public void testBadIsValidLicenseId()
{
assertEquals( "", true, m_fixture.IsValidLicenseId( 0 ) );
assertEquals( "", true, m_fixture.IsValidLicenseId( -1 ) );
assertEquals( "", true, m_fixture.IsValidLicenseId( "abc" ) );
}
...
}
何度も突っ込みたい衝動を抑えながら、内村がコードを打ち終えるまで待つ森川。
「よし、テストグリーン。問題ないですよね、森川さん?」
あっけらかんと聞いてくる内村。
「...え、えーと、いつもリファクタリングするときは、こんな感じ?」
「はい。ちゃんとテストも変更していますよ。」
「...いや、それは確かに大事なんだが...最初にテストを変更しなくて平気?」
「問題ないですよ。」
どうやら、最初の頃に教えたテスト方法を忘れてしまっているらしい。
(こりゃ、ただ説明してもダメかな?)
森川は、少し考えた後、リファクタリングしたロジックで失敗するようなテストコードを追加した。
「あっ!そのパターンは考えてなかった...」と少し焦る内村。
「ロジックの変更から行うと、こういうテストパターンの見落としに気付かないんだよ。」
「ふーむ。じゃぁ、森川さんはどうしてわかったんですか?」
「失敗するテストを考えたんだよ。」
「失敗するテスト?」
「そう。なんでかって言うと、最初からグリーンになってたら、どういうパターンでレッドになるかわからないでしょ?」
「はい...」
「最初にレッドが出てれば、それをグリーンにするためのロジックを修正すればいいってことになるよね。」
「え、でも、テストを後で書いても同じことじゃないですか?」
「でも、失敗するパターンは実際に確認している訳じゃないでしょ?現にこうしてテスト失敗した訳だから。」
「...」
やや腑に落ちない感じの内村に、森川はフォローを入れた。
「テストを書くのって、余分なロジックを書かなくてもちゃんと動作することを実証するためなんだよ。 ということは、リファクタリングをする前に”ちゃんと動作しないこと”を確認しておかないといけない。 でなければ、ちゃんと動作しているのかダメなのかわからないでしょ? そのためにも、まず最初はレッドを出して、それからロジックを実装してグリーンを出すことが大事なんだよ。」
「それって、すごく想像力が要る感じですよね?書き換える前にテストを直さなきゃいけない訳ですから...」
「そりゃそうさ!だってテストファーストって、詰まるところ、インタフェース設計だから。」
「?」
「テストを最初に書くためには、どんな引数があって、どんな戻り値があるのか?例外はどうやって処理されるのか? そういうロジックのIN/OUTを考えなきゃいけないでしょ?それってつまりインタフェース設計だよね?」
「...あ、そうか。確かにインタフェース設計そのものですね。」
「でしょ。で、最初にテストを書くことで、ロジックの中身に影響されることなく、インタフェース設計に専念できる。 それからロジックを書けば、最低限のロジックを実装すればいいってことになる。これっておいしいでしょ?」
「そっかぁ、なるほど〜。ユニットテストは、後でロジックをテストするためだけじゃないんですね。」
「そうそう。むしろ、素早くコーディングをするためにテストを書いた方がラクって感覚だと思うよ。」
そう言って、森川はキーボードを内村に返した。
ふと時計を見ると、すでにお昼過ぎ。
(今日も定時帰りは無理かなぁ?)
内村が”正しいテストファースト”で劇的にスピードアップしてくれれば...と明日に願いを託す森川だった。
前回:テスト可能な設計
There is no comment.