ブログトップ 記事一覧 ログイン 無料ブログ開設

やさしいデスマーチ このページをアンテナに追加 RSSフィード

札幌のWebエンジニアの綴る日常と開発の日々。
GoogleAppEngine/slim3/Django/NetBeans/Swing/JavaFXを中心にお届け。

2011-12-15

Developer Test から Customer Test へ #swtestadvent2011

| 23:17 |  Developer Test から Customer Test へ #swtestadvent2011を含むブックマーク

これは、「Software Test & Quality Advent Calendar 2011」の12/15分のエントリーです。ソフトウェアテストや品質に関するAdvent Calendarという事で、前のエントリーは@kz_suzukiさんの「それでもやはり、品質を「予測」したい」になります。

Developer Test と Customer Test

Developer Test と Customer Test とは、和田さんの講演の中で出てくる誰が何のためにテストを行うのかという観点で分類したテストの2つです。書籍「実践アジャイルテスト」の中でもテストの4象限モデルとして、ビジネス面/技術面の軸とチームの支援/製品を批評の軸によるテストの分類が紹介されていますが、ほぼ同じものと考えて良いでしょう。

Developer Testとは、開発者が開発者の為に、開発を行いやすくするためのテストであり、TDDユニットテストコンポーネントテストなどが該当します。テストはxUnitなどを使用して自動化され、プログラムの細かい部分をボトムアップでテストします。しかし、ユニットテストはプロダクトの品質を直接高めるテストではありません。結果としてプロダクトの品質は高くなる事はあっても、顧客にとってStringUtilsの数値フォーマットメソッドが正しく機能している事は、通常はビジネス上のメリットとならないからです。

一方、Customer Testとは、名前の通り顧客の視点に立ったテストです。顧客または顧客の役割を代行した開発者が、プロダクトの個々の機能が実現できているかを測るためのテストであり、プロダクト全体の進捗を知るためと考える事もできます。Customer Testは、インテグレーションテスト・ファンクショナルテスト・ストーリーテストなどとして実施され、顧客が理解できる形・内容で、トップダウンでテストします。Customer Testは、Cucumberのような専用のツールを使う場合もあれば、xUnitで行う事もあります。ユースケースシナリオをベースとしたテストなどでは、自動化が難しい場合もあるため、Developer Testよりは自動化の割合は減るでしょう。

尚、ユーザーエクスペリエンステストやユーザビリティテストといった見た目や使い勝手に関するテストは、Customer Testよりは品質に関するテストに近いモノです。見た目や操作感などはプログラムによる自動化テストは困難ですし、GUIのテストは苦労する割に効果が得られにくいものです。したがって手動で行う事が妥当となる事が多いでしょう。

プログラマが学ぶべきテスト

もし、あなたがプログラマであるのであれば、Developer Test から学ぶべきです。Developer Test の中でもxUnitファミリーを使用したユニットテストを学ぶべきでしょう。これは、先日の「TDDを学ぶべき10の理由」でも書いたようにプログラマとしてのスキルアップに繋がるからです。自分の中では開発とは「要求を実現すること」と定義していますが、実現するためには実現するための手段が必要不可欠です。実現する手段という手札を増やし、基礎体力を付けることは開発者として生きていくならば最初に身につけなければならないことです。そして、一生やらなければなりません。

はじめは、計算機やWiki構文変換などの単一の機能で閉じるユーティリティメソッドなどからテストをしてみましょう。入出力に注意し、どんなデータを必要とするのか、どんなテストがどの程度あれば安心できるのかなどを肌で覚えます。それに慣れたならば、少しだけ複雑な機能をテストしてみましょう。Listなどのコンテナ構造、状態を持つクラス、モックやスタブを使う必要のあるクラス、データベースなどフィクスチャが複雑なクラスなどです。このレベルになると、ほとんどのケースでテストはパターン化できます。パターン化し、「あぁ、こんなクラスのテスト書いたことあるわー。3年前に書いたわー」となる事が目標です。

ところが、ユニットテストを学んでいく中で壁を感じる時期を迎えます。なんとなくユニットテストを書く事には慣れてきたのだけど、少し大きいモジュールや機能になると、どこから手をつけていいのか解らなくなるのです。でも、安心してください。それは、次のステップに進む段階に来たという事です。

トップダウンで考える

ユニットテストの対象はクラスやモジュールといったプログラムの最小構成単位です。したがって、着目しなければならない範囲は小さいため、テストに必要な入出力や仕様の把握は比較的に簡単です。しかし、その部品がプロダクトの中でどのような位置づけにあるかについて忘れてしまう事があります。TDDを行った場合、顕著になるでしょう。TDDボトムアップでプロダクトを構築する開発手法であるため、意識的に全体像を見るようにしなければ、開発の中で迷子になってしまうのです。

つまり、プロダクトをトップダウンで見て開発する事も必要です。ほとんどのシステムでは、なんらかのインターフェイスがあり、顧客やタイマーなどの操作により入力が行われ、なんらかの出力が行われます。このシステムのインターフェイスは、ユーザが認識できるシステムの形になります。内部の細かいモジュールについて顧客は認識できませんが、画面やAPIといった外部インターフェイスは顧客が認識でき、ビジネス的価値をもたらすものです。したがって、外部インターフェイスを考え、外部インターフェイスのテストを書く事ができれば、それは1つの顧客に価値をもたらすゴールの1つとなります。また、そのゴールを目指すことが開発の目標となるのです。そのゴールを達成するために必要の無いクラスやモジュールはムダでしかありません。

ファンクショナルテストを作る

ファンクショナルテスト(機能テスト)は、Customer Testの1つであり、システムを外部から操作した時の振る舞いを検証するテストです。ただし、外部インターフェイスのテストですが、GUIに関するテストを含めてはいけません。システムにおいてGUIは重要な外部インターフェイスです。しかし、美しさ・ユーザビリティなど感覚に頼ったテストを行わなければならない部分でもあります。さらに、GUIは変更される可能性も高く、テストを書くコストも高いのです。

そこで、ビジネスの本質となる部分のテストを考えます。GUIはテストしないという方針を立てたならば、どこまでの処理が本質でテストすると効果的かを見極めます。ウェブアプリケーションであれば、コントローラ層のテストを行わずサービス層のみに集中して行うというのも1つの戦略です。それでは不十分ならば、コントローラ層も含めビューに依存しない形でテストを行う戦略もとれます。例えば、JSONで結果を返すREST的なAPIを別途用意してテストすることもできます。フレームワークのサポートなどがあれば、ビュー(テンプレート)に渡されるモデルについてテストも記述できるでしょう。上手くGUIに依存する部分を省き、エンドトゥエンドのテストを実行する事が目指す所です。ファンクショナルテストでは、どのようにどの程度のテストを行うかというテスト戦略を立てる事が重要です。

テストドライバーを作成する

ビューに依存したテストは書きにくいモノです。例えば、ウェブアプリケーションで、幾つかのフォームに値を入力して「送信」ボタンを押した時のエンドトゥエンドのテストを実現するのは厄介です。そのような時は、テストで実行したい操作を抽象化して定義されたテストドライバーを作成すると上手くいきます。ブラウザ上のボタンをクリックするのではなく、例えば次のようにプログラムとして記述できるフレームワークを作成するのです。

@Rule
WebTestDriver testDriver = new WebTestDriver("localhost", 8080);
@Test
public void 送信のテスト() throws Exceptipon {
    testDriver.loginWith(new Account("admin"));
    testDriver.open("/user/account");
	testDriver.forms.put("age", "30");
	testDriver.forms.put("favorites", "Java");
	testDriver.forms.submit();
	assertThat(testDriver.response, is(containsText("result: OK"));
}

裏側ではどのような事が行われているかというのは解りません。重要なのはテストを書くプログラマが抽象的にウェブアプリケーションの操作を行い、重要な部分について検証を行っているということです。

これでトップダウンのテストを書く事ができました。後はボトムアップで必要なクラスやモジュールを作っていくだけです。TDDを行うと効果的でしょう。

設計

もし、ゴールまでがとても遠い、大きな機能であるならば、もう数回の設計が必要かもしれません。設計とは適切な大きさで分割できるモジュールに分ける作業です。適切なインターフェイスを持つ幾つかのモジュールに分割してください。この時も、インプットとアウトプットに着目します。テストのスキルを高めることで設計力も高まるのです。さあ、各モジュールについてテストを書いてからモジュールを実装しましょう。

まとめ

テストと言っても大きく顧客に価値のあるビジネス面のテストと、開発者が開発を円滑にするための技術面のテストがあり、開発者にとってどちらも習得しなければならないスキルです。我々プログラマユニットテストなどボトムアップのテストに興味を持ちます。それは最初のステップとして重要な事です。ですが、それは開発者自身のためのテストであり、顧客のためのテストではないのです。開発者として顧客の要求を実現するには、顧客のビジネス的価値を検証するためのテストを書く事を意識しなければなりません。これらのボトムアップトップダウンのアプローチを上手く使い分ける事で品質の高いシステムを安心して開発できるようになるでしょう。

2011-12-05

JUnit のセカイ #JJUG

| 00:24 |  JUnit のセカイ #JJUGを含むブックマーク

このエントリーは、@cero-tさんのエントリーの次で、Java Advent Calendar 2011の6番目のエントリーです。自分自身の今年のメインテーマがTDDテスト駆動開発)と言う事もあり、関連エントリーとしてJUnitについて書きたいかと思います。今更JUnit?と思われた方も普段からJUnitを使っていあなたも気軽にお読みください。尚、色々な話題を駆け足で紹介するので、どれも簡単な紹介程度になってしまいますが、ご了承願います。

JUnit4 スタイル

JUnitアノテーションに対応し結構な月日が流れましたが、古いコーディング規約のままでテストコードを書いていませんか?JUnit4では、アノテーションアサーションを使ったテストコードを書くことが基本スタイルです。かつては、TestCaseのサブクラスを作り、testではじまるメソッドを定義していましたが、今は Testアノテーションを付与したメソッドがテストメソッドとします。また、Assert#assertThat を使い自然言語(英語)風に書くのがJUnit4 のスタイルです。

public class CalcuratorTest {
  @Test
  public void addに34を与えると7を返す() throws Exceptioon {
         // setUp
         Calcurator sut = new Calcurator();
	 int expected = 7;
	 // Excercise
	 int actual = sut.add(3, 4);
	 // Verify
	 assertThat(actual, is(expected));
  }
}

このように、4フェイズテストを意識し、テストメソッドに日本語を使い解りやすくすると良いでしょう。

Matcher

Matcherは、アサーション時に期待値と実測値が等しいことを検証するためのフレームワークです。Javaでは一般的にequals メソッドを使った比較検証が行われます。equalsの検証だけで問題がないのであれば、CoreMatchers#is メソッドを使用すれば良いでしょう。

しかし、複雑なオブジェクト検証を行う場合、単純なequalsの比較では情報が不足する事があります。例えば、何十行にも及ぶテキストを比較検証する時に、単純な文字列の比較だけでは一致しない事は解っても、何行目が一致しないかを地道に調べる必要が生まれます。そのような時には、カスタムMatcherの出番です。比較検証を行った上で、詳細な情報を出力する事ができます。

コンテキストベースのテスト

ユニットテストになれてくると、テストコードはテストの前提条件によってグルーピングする方が見通しが良くなると気付きます。例えば、データベースのテストをするのであれば、データベースが空の場合、1件のレコードがある場合、2件のレコードがある場合などに分け、それぞれのコンテキストで各メソッドのテストを実行するでしょう。そのような場合には、Enclosed テストランナーを利用したコンテキストベースのテストが便利です。

コンテキストベースのテストでは、static インナークラスを作成し、各インナークラスがテストクラスになります。それぞれでsetUpが行えるため、効率が良く見通しも良いテストコードが実現できます。

@RunWith(Enclosed.class)
public class ItemDaoTest {
  public static class データベースが空の場合 {
    @Before 
	public void setUp() {
	  // setup
	}
	@Test public void getListのテスト() {}
	@Test public void findのテスト() {}
  }
  public static class データベースに1件のレコードがある場合 {
    @Before 
	public void setUp() {
	  // setupとデータを1件投入
	}
	@Test public void getListのテスト() {}
	@Test public void findのテスト() {}
  }
}

テストケースが増えてくれば増えてくるほど強力な書き方です。

テストフィクスチャを工夫する

テストではテスト用のデータが重要な要素です。しかし、Javaは柔軟な記述を行える言語ではありませんので、データをどう管理するかは1つの課題です。色々な手段はありますが、強引にJavaで宣言的にデータの初期化を行う方法を紹介します。

	public void setUp() {
	  Item aItem = new Item() {
	    {
		  id = 10;
		  name = "Book";
		  price = 1580;
		  author = "hoge2";
		}
	  };
    }

外部定義ファイルで良いのであればYamlを使い snakeYaml を使ってロードすると良いでしょう。

  public static Fixtures load(InputStream input) {
    return (Fixtures) new Yaml(input).load();
  }

パラメータ化テスト

テスト対象のクラスによっては、同じメソッドを様々な値で検証したい場合があります。このような場合、コピペを繰り返してテストを書く事もできますが、パラメータ化テストを行うと綺麗に書くことができます。JUnit ではコンストラクタでテストパラメータを受け取る方法と、テストメソッド引数にテストパラメータを受け取る方法がありますが、ここではテストメソッドの方を紹介します。

@RunWith(Enclosed.class)
public class JyankenTest {
  @RunWith(Theories.class)
  public static class 引き分けになるパターン {
    @DataPoints
    public static Jyanken.Hand[][] getParameters() {
        return new Jyanken.Hand[][] {
		  {Jyanken.Hand.GU, Jyanken.Hand.GU },
		  {Jyanken.Hand.TYOKI, Jyanken.Hand.TYOKI },
		  {Jyanken.Hand.PA, Jyanken.Hand.PA }
	   };
    }
    @Theory
	public void judgeは0を返す(Jyanken.Hand[] hands) throws Exception {
	    String msg = hands[0] + " vs " + hands[1] + " should be even.";
		assertThat(msg, sut.judge(hands[0], hands[1]), is(0));
	}
  }
}

パラメータ化テストでは、どのパラメータがエラーになったかを解らないため、assertThatの第1引数にメッセージを渡すなどの工夫が必要です。

assumeThat

assumeThatはassertThatとほぼ同じ構文をとる検証メソッドですが、assumeThatではマッチしない場合にそのテストは失敗とならずにスキップされます。この性質を利用すると、特定の環境(Windowsのみなど)で実行するテストを書く事ができたり、パラメータ化テストにおいてパラメータは全組み合わせを行う一方で、テスト毎に特定の条件を指定してパラメータフィルタリングを行うなどの手法をとることができます。

Rule と ClassRule

Rule と ClassRuleは強力なJUnit の拡張フレームワークです。Rule を使う事により、各テストの実行前や実行後にAOPのような感覚で処理を挟む事ができます。例えば、外部アプリケーションサーバRDB)の起動や初期化処理などです。他にもJUnitメタデータアクセスできるため、テストをより細かくコントロールする事ができます。

Rule は自分で作成する事でユニットテストのコードを大きくリファクタリングできますが、デフォルトでいくつかのRuleも提供されています。必ず破棄されることが保証されるTemporaryFolder、テスト実行後の事後条件を検証するVerifier、実行中のテストメソッドの名前を取得できるTestNameなどです。

public class TemporaryFolderExcampleTest {
    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    @Test
    public void mkDefaultFilesで2つのファイルが作成される() throws Exception {
        File folder = tempFolder.getRoot();
        TemporaryFolderExcample.mkDefaultFiles(folder);
        String[] actualFiles = folder.list();
        Arrays.sort(actualFiles);
        assertThat(actualFiles.length, is(2));
        assertThat(actualFiles[0], is("UnitTest"));
        assertThat(actualFiles[1], is("readme.txt"));
    }
}

カテゴリ化テスト

テストケースが増えてくるとテストの実行に時間がかかり、フィードバックが遅くなる問題(スローテスト問題)にあたります。そのような時には、Category の機能を使い、テストにタグ(カテゴリ)を付け、カテゴリによって実行する(しない)テストケースを選択します。次の例では、SlowTestsのタグが付いたテストはCategorizedTestの実行時にはスキップされます。

@ExcludeCategory(SlowTests.class)
@SuiteClasses({ FooTest.class, BarTest.class })
public class CategorizedTest {
}

public interface SlowTests {
}

@Category(SlowTests.class)
public class FooTest {
    @Test
    public void test01() throws Exception {
        System.out.println("FooTest#test01");
    }
    @Test
    public void test02() throws Exception {
        System.out.println("FooTest#test02");
    }
}

public class BarTest {
    @Test
    public void fastTest() throws Exception {
        System.out.println("BarTest#fastTest");
    }
    @Category(SlowTests.class)
    @Test
    public void slowTest() throws Exception {
        System.out.println("BarTest#slowTest");
    }
}

終わりに

いかがでしたでしょうか?どのくらいの機能を使っているでしょうか?オススメは、テストのコンテキスト化とRuleによる拡張です。JUnitの可能性が一気に広がりますよ。

以上、簡単にJUnitの知らないかもしれないセカイを色々と紹介してみました。もし、もっと詳しくそれぞれの内容を知りたいのであれば、はてブに「わっふるわっふる」とコメントしてみてください。

明日は、札幌から一気に日本の反対側まで飛び、沖縄の@o_tmrさんのエントリーです。

2011-12-04

TDDを学ぶべき10の理由 #TddAdventJp

| 00:09 |  TDDを学ぶべき10の理由 #TddAdventJpを含むブックマーク

かなり香ばしいタイトルですが、TDD Advent Calendar jp: 2011のエントリーとなります。前日の@bleisさんのエントリーの次になります。

はじめに

TDDテスト駆動開発)とは、「テストファーストを原則とし、テストが成功するようにプロダクションコードを書くというサイクルを繰り返す開発手法」です。XPプラクティスの1つとして10年近く前に紹介され、ここ数年で再び1つのムーブメントとなっています。これは、TDD Boot CampTDDへの敷居を下げ、体験する機会を提供した事も1つの大きな要因でしょう。

自分もTDDに魅せられたエンジニアの1人です。ぶっちゃけ、TDD信者とかTDD厨とか言われても可笑しくはありませんし、むしろ嬉しいくらいです。一方で、TDDを嫌う人もいるのも事実です。しかし、自分もTDD銀の弾丸とは思っていませんし、適用しにくい領域もある事も理解しているつもりです。それでも、プログラマTDDを学ぶべきだし開発で実践するべきというスタンスは崩れません。

今日のエントリーは、なぜTDDを学ぶべきなのかという理由について考察してみたいと思います。一部ユニットテスト自体のメリットも含まれます。

1.フィードバックと安心

TDDで開発を行わないとした場合でもユニットテストの最大のメリットは、プログラムとしてテストが可能となる事です。これは、テストを手動で実行する場合に比べて、テストコードを作成するコストはかかりますが、実行コストを限りなくゼロにする事が出来き、何度でも繰り返してテストを実行できるようになります。したがって、コードの追加や修正毎にテストを実行する事ができます。

このように継続的にユニットテストを実行できるようになれば、コードの追加や修正が他のコードに影響を与えている場合にテスト失敗として認識できるようになります。この問題の素早いフィードバックがあるため、開発者は安心して新機能の追加や仕様変更を行う事ができるようになります。

2.リファクタリング

ユニットテストがある事で、素早いフィードバックを得る事ができるため、変更に対する恐怖がなくなります。これは、積極的にリファクタリングが出来るという事です。

リファクタリングとは、「プログラムの外部的な振る舞いを変えずに、内部構造を変更すること」です。ユニットテストトは「プログラムの外部的な振る舞い」を検証する事に他ならないため、ユニットテストが成功する事を維持するのであれば、安心して内部構造を変更できるのです。

ソフトウェアが健全な状態を保つには、定期的なリファクタリングは必要不可欠です。開発がYAGNI(You ain't gonna need it)の原則に従っていれば、必要になってから必要なものを付け足していくはずです。したがって、はじめはシンプルなものであっても次第に複雑になるのです。一般的に、開発が進めば進むほどプログラムは複雑になるでしょう。デザインパターンなども最初から適用するのではなく、必要になってから適用する方が良いケースが多いです。そのような時にユニットテストがある事で、大胆な内部構造の変更を安心して行えるのです。

定期的なリファクタリングを行い、ソフトウェアを少しずつ作っていくことと健全な状態に保つ為にはユニットテストが必要です。

3.インターフェイスへの意識

TDDではテストファーストが原則です。プロダクションコードよりも先にテストコードを書くため、そのクラスがどのようなメソッドを持つかを考えなければなりません。メソッド名・引数戻り値副作用などを意識し、先にテストコードに書いていくため、自分が最初の利用者となります。使いにくいインターフェイスであれば自分を苦しめます。

コードを書くときにインターフェイスを意識するべきというのは当然の事のようにも思えます。TDDを行わない場合、特にプログラミングの経験が浅い人ほどインターフェイスの設計よりも先に実装したい機能に意識が向いてしまいます。その意識を強制的に「どう使われるか」に向けるのがTDDテストファーストなのです。

4.シンプル設計

TDDではテストコードを書き、そのテストを成功(グリーン)にするために最速な実装を行う事が原則です。これは、TDDのサイクルを短くしリズム良く開発を進める効果も大きいですが、最大の効果は実装がシンプルとなる事です。シンプルであるほどバグが少ないことは言うまでもありません。

また、TDDではテストコードを書かなければならないため、設計をシンプルに保たなければテストが書きにくくなります。特にクラスの結合度が高い場合や内部状態を持つ場合などは、疎結合で内部状態を持たないクラスに比べてテストが複雑になり避けるようになります。TDDを行っていると常にテストし易さ(テスタビリティ)を意識してクラス設計を行えるようになります。内部状態や結合度が強い部分は、本当に必要な部分だけになるでしょう。

TDD実践する事で、無駄なコードが少なくなり、ソフトウェア全体がシンプルになります。

5.メンテナンスされたドキュメント

私はドキュメントを書くよりもコードを書いていた方が幸せです。できれば、面倒なドキュメントを書きたくはありませせん。基本設計やユースケースレベルのドキュメントであれば苦ではないのですが、APIレベルのドキュメントを書くのはもの凄く億劫です。恐らくは、「それコードで書いた方が早いし、楽だよ...」と感じてしまうからだと思います。

そう、TDDを行う事で、APIのドキュメントは動くテストコードとして書くことが出来るのです。具体的な入力値と期待値もドキュメント化され、それが正しく実装されている事も保証されます。また、テストコードはそのAPIのサンプルコードとしても読むことができるため、使い方が解らなかったらテストコードを読んでみるという事が出来るようになります。

さらに、ドキュメントは書くことよりもメンテナンスする事の方が難しいものです。仕様が変わった時や追加された時にドキュメントの更新を忘れる事は良く発生しますし、ドキュメントを更新しなくともソフトウェアの不具合とはなりません。後日、そのドキュメントを読んだ人が誤解してしまい、大きな不具合を混入させるかもしれないというリスクだけが蓄積されていくでしょう。

ドキュメントがテストコードとして作成されたならば、メンテナンスは必要不可欠となります。もし、メンテナンスしなければテストは失敗するようになるでしょう。

TDD実践する事で、必要なドキュメントをメンテナンスされた状態で持てる事になります。

6.具体的なテストデータ

ユニットテストを行う時には具体的なテストデータを準備しなければなりません。例えば、「加算する」ではなくて「3と4を加算すると7となる」と言ったように具体的な値がなければテストケースになり得ません。些細な事と思う人もいるかもしれませんが、ソフトウェア開発では具体的でリアルなデータの有無は品質と生産性に直結する重要な要素です。

具体的でリアルなデータが重要な事はユニットテストだけに限らず、ユースケースシナリオ・機能テスト・受入テストなどの作成でも同様です。ユースケースシナリオ等では、リアルなデータである事により顧客が理解しやすくなります。例外となる状況や見落としてしまうようなビジネスルールに気付くこともあるでしょう。ユニットテストでも同様で、開発者同士で具体的な値を示してコミュニケーションを行う方が相互理解が深まります。

TDDを行えば、プロダクションコードを書く前にどんなデータが必要かを考える事が自然となります。

7.習得可能なスキル

「メリットは解った。でも、難しいんでしょう?」

はい。TDDは簡単なスキルではありません。TDDを行うには、テストに関するスキル・設計に関するスキルリファクタリングに関するスキルなど多くの知識・スキルが必要です(see: TDD の基礎体力と、TDD に対する想い)。TDDのやり方を知ったとしても、自然にテストを書くようになるには練習が必要です。

しかし、安心してください。TDDは「量が質に転化する」スキル(by @t_wada)です。TDDと関連する基礎スキルを伸ばすことはプログラマとしてのスキルを伸ばすことに繋がります。プログラマとしてのスキルを伸ばす必要がない人にはTDDは不要ですが、TDDはテストスキル・設計スキルリファクタリングスキルを伸ばすには都合が良い開発手法です。

TDDを効率良く学ぶには、経験者とペアプログラミングする事が最も効果的です。経験者がどのように考えて、どコードを書いていくかを感じましょう。職場でそのような環境がないのであれば、TDD Boot Campに参加して、まずは体験する事が良いと思います。勿論、独学でもTDDは習得できます。「テスト駆動開発入門」や「和田卓人の“テスト駆動開発”講座」を参考に写経してみることです。

TDDに関するスキルが習得可能である大きな理由は、ある程度はパターン化可能だからです。プロダクションコードは要求次第でどのようにも変わる可能性があります。しかし、ユニットテストのパターン、設計に関するパターン、リファクタリングに関するパターンは1つづつ習得していく事が可能なパターンです。量が質に転化するのは、パターンの引き出しが増えるからです。気がつけば「これはこうする」といった思考が自然にできるようになるでしょう。

TDDプログラマとしてのスキルを確実にアップさせる事ができます。

8.環境へ非依存

アジャイル開発を行おうとすれば、顧客を巻き込む事が必要不可欠です。これは様々な理由で高いハードルとなります。しかし、TDDは開発手法の1つでしかありません。言い換えれば、どのようにしてプログラミングを行うかという事です。したがって、あなたの開発現場で明日からでもTDDをはじめる事ができます。

勿論、TDDの効果を実感できるまでには相応の経験と知識が必要です。しかし、導入するだけであれば、開発チームの合意を得られれば、それこそ1人でこっそりテストコードを書くこともできます。

TDDは開発者の気持ち次第で簡単に導入できます。

9.たくさんコードを書ける

プログラマであれば、コードをたくさん書きたいと思いませんか?

TDDではテストコードを大量に書く事が求められますが、言い換えればたくさんのコードを書くことができるという事です。テストコードもプログラムですから、工夫して整理しなければメンテナンス性を損ないます。効果的なライブラリやツールを作成したりする事もできます。これらのコーディングはプロダクションコードのコーディングよりもずっと自由があるでしょう。

プログラマであれば、コードを書くことは楽しい事です。TDDを導入すればたくさんコードを書けるようになります。

10.開発が楽しくなる

追い詰められた状態で開発をしても、生産性はあがるどころか下がるだけです。後からテストを行ってたくさんの不具合に怯え、デグレに気をつけながら修正せいたり言い訳を考えるのは気が滅入ります。それよりも、TDDを導入して安心を手に入れましょう。

安心を手に入れ開発に余裕が生まれれば、自然と生産性も高まり品質も高まります。顧客からの仕様変更も笑って受けられる体制であれば、お互いに幸せになれるはずです。それをコードをたくさん書くことで得られるのであれば、プログラマにとって、とても幸せな事です。

まとめ

TDDは、銀の弾丸ではありません。GUIのテスト、仕様が曖昧であったりプロトタイプとして作成しているソフトウェアなどには仕様変更による修正コストが高くつくため適用範囲を見極める必要があります。TDDを行ったからと言って品質が高まる保証もありません。プログラマスキルが高まり安心して開発できるようになるため、結果として品質が高くなります。また、テストなんか書かなくても神のような速度で完璧なコーディングができる人やプログラマとしてスキルを高めたくない人には効果が薄いでしょう。

TDDは、自分のスキルを高めたいプログラマにとっては、楽しく効率良くスキルアップできる強力な開発手法です。今日からでもTDDを学び、そして広めていってください。開発現場をもっと楽しくしましょう!

次は、@sue445さんのエントリーです。

2011-11-07

TDDBC 横浜に参加してきました

| 12:49 |  TDDBC 横浜に参加してきましたを含むブックマーク

10/5に開催されたTDDBC横浜に札幌から参戦してきました。わざわざ札幌から?と不思議に思われるかもしれませんが、福岡からもちょくちょく首都圏のTDDBCに参加されている人もいますのでそれほど不思議なことでもありませんw自分の場合、年に1〜2回程度は、東京で開催されている勉強会やカンファレンスに参加するようにしています。理由は地方で閉じこもっていると視野が狭くなってしまうこと、特にコネクションが閉じてしまうのが大きなマイナスになっているからです。今はTwitterなどで全国各地で活躍されている人とネット上のコミュニケーションが可能ですから、思い立って地方から参戦しても完全なぼっちは避けられるのはうれしい事かと思います。

往路

札幌からスカイマークの最終便で羽田に入り、横浜まで移動してスパ(カプセルホテル+風呂)というプランで考えていましたが、いきなり大変なことに・・・。新千歳発の最終便の羽田着は24:15くらいなのですが、到着時点で定刻だとしても移動手段(深夜バスや電車)が一切ないのです。東京(横浜)は5−6年くらい住んでましたし、その前に埼玉にも4−5年住んでましたから、「まさか0時ちょっとで移動手段がなくなる」という事を想像できませんでした。結局、国際線ターミナル(ここだけ24h営業)へ移動して仮眠するという事になります。この事実を知っている人にとっては最終便で移動して朝まで国際線ターミナルというのが基本らしいですが、知らなかったという人は多いようでよくトラブルになっているそうです。結局、横浜までタクシー使ったら1万と聞いて、移動は諦め、国際線ターミナルでもくもくと1人TDDBC羽田を開催することになったのです。まあ、翌日のLTのネタに出来たので面白かったと考えておきます・・・。

受付してました

今回の参加目的の1つとしては、TAなどではなく自分もペアプロをしたいという点です。なので、TAには立候補しませんでした。しかし、何も協力しないでは面白くないので会場の受付をやっておりました。名前の方を確認していた方が私です。

もし、このようなイベントであまり知り合いがいないようであれば、受付をやるのは良い選択です。まず開会までの間、ぼっちにならないという最大のメリットがあります。また、ほとんどの参加者と形式的とはいえ会話できま、ある程度の顔を認識してもらえるのは大きいです。「あ、受付の人だ」というのは良くある話でしょう。なんで札幌から来て横浜で受付やっているかは謎ですがw尚、自分は和田さんの基調講演は生で2回聴いているということもあり基調講演の最初の方を聞けませんでしたが特に問題ありません。というわけで、やらせていただけて有り難うございます。

基調講演

何度も書いているし、他の人も書いているので省略します。1つだけ書いておくことがあるとすれば、TDDの効果について一言で説明するとき、「実装が2割増えて、バグが半分になる」は過去のもので今は「実装が2割増え、バグが7割減る」というくらいで問題ないと話されていた事です。各所での実績やスキルアップ・研究などの成果は出てきているという事でしょうね。

ペアプロデモ

公開きゃっきゃうふふを堪能しました。あまりにもけまらしい内容で、レポートに起こしたら負けだと思います。

昼食+LT

少しでも多くの演習時間を確保するためにも昼食はお弁当というのがTDDBCの定番となっていますが、横浜でもお弁当でした。また、ランチを取りながらLT(ミニセッション)を聴くというスタイルです。時間の有効活用としては良いのですが参加者同士のコミュニケーションは少なくなってしまうかもしれません。札幌ではランチはチーム(ペア)毎に仕様確認・開発環境の準備・自己紹介などのコミュニケーションに当てています。どちらが良いでしょうかね?

私もLTでは、「テストリストの見つけ方」という題目で10分程度話させていただきました。

内容はスライドにだいたい書いてあるのですが、これまでのTDDBCの経験から、最初にどの辺からとりかかるか、そして気付くと細かすぎる所に迷い込んで上手くまとまらないといった状況に多く遭遇しています。それはどうしてか?どうすればいいのか?という答えの1つが、TDDBC札幌2.1で行ったユースケースをベースとしたBDD(振舞駆動開発)なわけです。システム全体に必要な入出力をキーに最初は全体を把握し、細かく分割・設計・テストを行っていく事が重要という事です。また、入出力は具体的な値を使う事が大切であり、設計(テスト)を書くときには必ず意識する事を話しました。懇親会とかで聴く限りはなかなか好評だったので良かったと思います。ただ、そこそこのボリュームがあるにも関わらず10分程度で話すためにかなり早口&圧縮になってしまったのは残念でした。じっくり聴きたいとかユースケース駆動で受入テスト駆動でのBCをやってみたい人達がいましたら呼んでくださいw

演習

先にも書きましたが、自分はTAではなくJavaペアプロ参戦しました。白状してしまうとGroovyも凄くやりたかったんですが、JUnitで色々試したい事があってJavaを選択してます。今回は、こんな事にチャレンジしてみました。

  • 最初に機能テスト(FunctionalTest)を作成する
    • システムをブラックボックスとしてテストし、システムが機能要求を満たしているかを確認する
    • 選手データの入力と打率ランキングの表示
    • フィクスチャはyamlに定義
    • テストを実行するためのテスター(エージェント)を作成
  • Enclosedを使い、テストの階層化を実現
  • Theoriesを使い、パラメタライズドテストを作成
  • Mercurial + JenkinsCI でローカルCI環境
  • 成果物はBitbucketへpush

流石にやりすぎ感は否めませんが、機能テスト部分はある程度の予習をしていたので、最初から一気に飛ばしまくりで演習を行っています。成果物は、Bitbucket にpushしています。

今回はペアプロということでパートナーと交代しながら演習をしましたが、ギアの入れ方の緩急を付けることを意識しました。基本的には「小さく少しずつ」がTDDのこころですが、それだけでは幾ら時間があっても足らない事がよくあります。したがって、慎重にやる箇所と一気にギアチェンジして進めるところ、そして少しでも気になる部分があればまた慎重に行く、といった緩急をつけたリズムが出来上がると集中力も高まります。疲れますけどw

レビュータイム

他の言語のテストコードを見るのは面白いです。Rubyのテストコードは読みやすいよなといつも嫉妬しながらRSpecを見ていますが、今回はGroovyの存在感が凄かったです。おかげでJava屋にも希望の光が見えたのではないでしょうか?あそこまで綺麗に書けるならば、導入する価値は高いですね。とはいっても、自分の場合はまだまだJUnitを使い倒し、綺麗に書く工夫の余地は多く残っていると思いますから、もうちょっとだけJavaをやりたいです。いえ、Groovy本は持っていて少しずつ読んでいますがw

懇親会

懇親会は会場のある施設内のレストランを借り切って行われました。参加者の7割くらいは参加されていたのではないでしょうか?あまり多くの人と話せなかったのはやや残念でしたが、2次会では「俺がTDDだ」「こんな○○さん、みたくなかった」など名言も多く、楽しい時間を過ごさせていただきました。

運営的な部分

TDDBCは参加希望者が凄く多く、首都圏での開催では、定員30名程度の所に100名以上の参加希望があったりすることが珍しいイベントではありません。地方ではスカスカであるというのはちょっと悲しいところですが…。ただ、今回は先着でなく抽選ということで概ね好評だったようです。ただ、受付をしていたのでだいたい把握できていたのもありますが、連絡の無い欠席者が結構居たのは残念ですね。予定が合わなくなったなど理由があるのは仕方ないかと思いますが、連絡をすることは最低限のマナーでしょう。その連絡により別の参加できたかもしれない人がいるということを肝に銘じるべきです。あと、お弁当も幾つも余ってしまっており、非常に勿体ないです。こうやってみると札幌の勉強会の欠席率は本当に低いな・・・と思います。

会場はネットワークが厳しい点以外は凄く良かったと思います。ただ、自分としてはTDDBCを行うならば会場の選定基準はネットワーク重視としています。Webで情報を検索したり、GitBitbucketなどのリポジトリにソースを公開できたりと移動の手間よりも多くのメリットがあると感じました。Twitterへの投稿も少な目だったのはその辺も大きく影響しているかと思います。

演習のお題は非常に良かったと思います。やや業務ドメイン(野球知識)に偏る部分はありますが、現実世界の業務でもそんなものです。全く知識無しって人もそれほどいないでしょうからテーマとしては最適ですね。同じようにボーリングのスコアとかサッカーの勝ち点なんかも面白いかもしれません。ただ、課題の量と質に比べて、もう少しだけ演習の時間を多くやりたかったと感じました。だいたい13:30くらいから開始で、18:00くらいまでが演習だったので、4時間程度です。自分は予習をしておいたので、スムーズに進んだ方かとTAの負担や参加者の燃焼度としてはもう少し時間が欲しい。尚、札幌でのスタイルとして、講演あり演習少な目という回と講演なし丸1日演習の回をうまく使い分けようかなと考えています。

最後にペアプロによるTDDのデモについてです。非常に楽しくニヤニヤさせてもらいましたが、やはり初TDDの人にとっては厳しいと言わざるを得ないでしょう。実はこれ、札幌の2.0でも同様のミスを犯しておりまして、いつもやっているし大丈夫だろう・・・と思っていたら結構グダグダになってしまったのです。理由は色々あるのですが、やはり一番の問題はデモという特殊な状況に慣れていない事かと思います。デモコーディングは思った以上に緊張するので、慣れないと伝えたいことが全く伝わりません。準備もされていた2人と思いますが、思ったように進まないでやきもきしているのは伝わってきました。そして、このデモがTDDBCの一番の肝だと思います。和田さんの基調講演(座学)は概念的なものが多く、午後の演習では具体的にどうするの?という問題が発生します。その架け橋をがデモなのです。自分もまだまだデモでは緊張が抜けないので、スキルと経験を高めたいところです。

その他

当初の予定では日曜日の午後くらいに札幌に戻る予定でしたが、日曜日に昇格争い中のコンサドーレの試合があったため、予定を変更して月曜日の始発で帰るという強行軍となりました。試合は残念ながら負けてしまいましたけど、アウェイなのにホームと同じくらいの動員数と盛り上がりには驚きました。たまには地元チームの応援ってのもいいですね。尚、帰りの便は月曜日の始発(6:20発新千歳行き)でした。札幌駅に到着したのは8:45頃でそのまま健康診断だったとかw

終わりに

というわけで札幌から横浜までTDDBCに参加してきましたが、横の繋がりや知らない人の交流は非常に有意義で楽しいものでした。財政的にいつもは難しいですが、年に2回くらいは勉強会で上京したいですねー。

2011-10-01

TDD Boot Camp 札幌 2.2 を開催します #tddbc

| 08:47 |  TDD Boot Camp 札幌 2.2 を開催します #tddbcを含むブックマーク

2週間ほど前にTDD Boot Camp 札幌 2.1が開催されたばかりですが、11/3にTDD Boot Camp 札幌 2.2を開催します。

ATNDの開催概要にも記載していますが、札幌では6回目のTDDBCとなります。TDDBCは、テスト駆動開発の体験のためのイベントです。しかし、実戦で使えるためには反復的な練習と、レガシーコード改善や受入テスト駆動開発など、TDDから1歩前に進んだテーマも必要であり、TDDBC札幌では様々なアプローチをしてきたと思います。前回の2.1では参加者も皆TDDに慣れ始めたと感じました。

そこで、1回基本に返る事をテーマとしてTDDBC札幌2.2を開催したいと考えています。これまでのTDDBCに参加した人であれば、これまでの成果を試す場所、初めてのTDDBCであれば経験者と一緒に体験できる場所、というのが目標です。お題もあえて、1.0の時に使用したものを使いたいかと思います。札幌1.0に参加した人は、自分のTDDBCスキルがどれだけ変わったかを実感できるのではないでしょうか?

というわけで、ATNDで募集していますので、興味のある方は是非参加ください。