TDD Boot Campに参加しました
【追記】今回作成したコードを最下部に追記しました。
まずは、主催者・スタッフの方々にお礼を言いたいと思います。講演だけでもすごく勉強になったのに、こうやって演習の時間まで設けてもらうのはすごく準備等に時間や手間がかかったことでしょう。本当にお疲れ様です。
私個人としては、各テーブル内で一番よかったと思う人をテーブル内メンバー6人で決める「テーブル賞」に推薦していただきました!まだまだ未熟な自分というのは自覚していますが、こうやって認められるのは素直に嬉しい限りです。ありがとうございます♪
で、グループ賞として技術評論者様から本一冊をいただける権利をもらいました。あまり選択する時間も無かったので、前から気になっていた「パターン・Wiki・XP」の本を選びました。が、後々考えると会場で宣伝があった「スクラムでアジャイルプロジェクト管理」を選んでいればよかったかなー。
パターン、Wiki、XP ~時を超えた創造の原則 (WEB+DB PRESS plusシリーズ)
- 作者: 江渡浩一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2009/07/10
- メディア: 単行本(ソフトカバー)
- 購入: 74人 クリック: 1,217回
- この商品を含むブログ (143件) を見る
実践!アジャイルプロジェクト管理 -スクラムではじめる最強エンタープライズ開発-
- 作者: 株式会社テクノロジックアート,長瀬嘉秀,設楽秀輔
- 出版社/メーカー: 技術評論社
- 発売日: 2009/10/14
- メディア: 単行本(ソフトカバー)
- 購入: 1人 クリック: 27回
- この商品を含むブログ (13件) を見る
基本的に演習や講演に集中しててあまりログは取っていないけど、覚えている範囲で。
Lasse氏の講演
- TDDやるとコードは15%増しになるけど、欠陥率が最大90%減になる
- レガシーコードは最初にテストを作る
- レガシーコード本にも繰り返し述べられていたこと(⇒ レガシーコード本 読了! - @ikikko のはてなダイアリー)
- デモ:coberturaに機能追加する*1
- テストできそうな箇所を小さい範囲にメソッド抽出
- さらに、副作用がある箇所をprotectedメソッドに抽出
- サブクラスで副作用メソッドをオーバーライドして無効化
- テストのために、検出用変数をprivateからpublicに変更
- 検出用変数にアクセスして、assertを記述
スポンサーセッション
- IBM Rational Team Concert - Japanが気になりました
- 体験版DVDをIBMさんのブースでもらいました
TDD演習
Lasse氏のTDDコード
- テストコードが洗練されてて綺麗
- テストに関係ないものは、徹底的に排除する
- Mapを扱うお題だけどvalueの値自体はテストに関係ないので、put(key, key)みたいなヘルパーメソッドを作る
- 時間関係のテストはFakeを使って、テストをやりやすくする
振り返り
- 環境周りを整備すればよかった
- 今回のTDD演習の内容は基礎として身につけておきたい
- 実業務になると、もっと多くの要因が絡んできてそれに対処しなければならないことが多くなるだろうから
- 例えば今関わっている携帯アプリの開発だと、実機を使う部分と使わなくてもよい部分をいかにして切り分けてテストしやすくするかが大事
今回のTDD演習で作ったコードです。途中で時間切れになったのでgdgdなところも多々ありますが、晒しておきます。
package lrucache; import java.util.*; public class LRUCache { private static final int DEFAULT_SIZE = 2; private final Map<String, String> map = new HashMap<String, String>(); private final List<String> list = new ArrayList<String>(); private int size; public LRUCache(int i) { size = i; } public LRUCache() { this(DEFAULT_SIZE); } public void put(String key, String value) { if (map.size() >= size) { map.remove(list.remove(0)); } map.put(key, value); updateList(key); } public String get(String key) { updateList(key); return map.get(key); } private void updateList(String key) { list.remove(key); list.add(key); } public String getNewest() { return list.get(list.size() - 1); } public void setSize(int newSize) { int currentSize = list.size(); if (newSize >= this.size || currentSize <= newSize) { this.size = newSize; return; } this.size = newSize; for (int i = getStartIndex(); i >= 0; i--) { map.remove(list.remove(i)); } } private int getStartIndex() { return list.size() - this.size - 1; } }
package lrucache; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import org.junit.*; public class LRUCacheTest { // --------------------------------------------------- 最初の仕様 @Test public void putとgetができること() { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); assertEquals("A", cache.get("a")); assertEquals("B", cache.get("b")); } @Test public void 最初にputしたものが削除されること() { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.put("d", "D"); assertNull(cache.get("a")); assertNull(cache.get("b")); assertEquals("C", cache.get("c")); } @Test public void getしたやつが一番あたらしいこと() { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.get("a"); assertEquals("a", cache.getNewest()); cache.put("c", "C"); assertEquals("c", cache.getNewest()); } @Test public void 最初にputしても途中でgetされたものは削除されないこと() { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.get("a"); cache.put("c", "C"); assertEquals("A", cache.get("a")); assertNull(cache.get("b")); assertEquals("C", cache.get("c")); } @Test public void 最初に最大値を指定できること() throws Exception { LRUCache cache = new LRUCache(3); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.put("d", "D"); assertNull(cache.get("a")); assertEquals("B", cache.get("b")); assertEquals("C", cache.get("c")); assertEquals("D", cache.get("d")); } // --------------------------------------------------- 仕様変更1 @Test public void 最大値を途中で増やせること() throws Exception { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.setSize(3); cache.put("d", "D"); assertEquals("B", cache.get("b")); } @Test public void 最大値を途中で減らせること_満タンの時() throws Exception { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.setSize(1); assertNull(cache.get("a")); assertNull(cache.get("b")); assertEquals("C", cache.get("c")); } @Test public void 最大値を途中で減らせること_減らしたときに上限までいっていないとき() throws Exception { LRUCache cache = new LRUCache(3); cache.put("a", "A"); cache.setSize(1); assertEquals("A", cache.get("a")); cache.put("b", "B"); assertNull(cache.get("a")); assertEquals("B", cache.get("b")); } @Test public void 最大値を途中で減らせること_減らしたときに上限を超えているとき21() throws Exception { LRUCache cache = new LRUCache(3); cache.put("a", "A"); cache.put("b", "B"); cache.setSize(1); assertNull(cache.get("a")); assertEquals("B", cache.get("b")); } @Test public void 最大値を途中で減らせること_減らしたときに上限を超えているとき42() throws Exception { LRUCache cache = new LRUCache(4); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.put("d", "D"); cache.setSize(2); assertNull(cache.get("a")); assertEquals("D", cache.get("d")); assertEquals("C", cache.get("c")); } @Test public void 最大値を途中で減らせること_減らしたときに上限を超えているとき53() throws Exception { LRUCache cache = new LRUCache(5); cache.put("a", "A"); cache.put("b", "B"); cache.put("c", "C"); cache.put("d", "D"); cache.put("e", "E"); cache.setSize(3); assertNull(cache.get("a")); assertNull(cache.get("b")); assertEquals("E", cache.get("e")); assertEquals("D", cache.get("d")); assertEquals("C", cache.get("c")); } @Test public void 同じものをputしたときも新しくなること() throws Exception { LRUCache cache = new LRUCache(); cache.put("a", "A"); cache.put("b", "B"); cache.put("a", "A"); cache.put("c", "C"); assertEquals("A", cache.get("a")); } // --------------------------------------------------- 仕様変更2 @Test public void putまたはgetしたときの時間を取得できること() throws Exception { fail(); } @Ignore @Test public void 時間切れのオブジェクトを削除できること() throws Exception { fail(); } }