Ruby
オブジェクト指向
リファクタリング
テスト
アジャイル

ユニットテストの歴史とテスタブルなコードについて

More than 1 year has passed since last update.

自己検証コードの歴史

  • 「自己検証コード」の黎明期は1992年のOOPSLAであるらしい
    • ACMが毎年開催しているオブジェクト指向の国際会議である
    • 誰かがここでのスピーチで「クラスは自分自身の検証コードを含んでいないといけない」と言ったそうである
    • 「技術的負債」という言葉が歴史的に初めて使われたのもここでのことらしい
      • wikiwikiを作ったカニンガムが使ったそうである
  • ケント・ベックの「エクストリーム・プログラミング(1999)」、マーチン・ファウラーの「リファクタリング(2000)」の出版以降、一般に自己検証コードの重要性が広まることになる
    • 「エクストリーム・プログラミング」は、従来のウォーターフォールモデルへのアンチテーゼであり、継続的なデプロイにより、品質の高いソフトウェアを作るテクノロジである
      • そのためにはテストコードが必要なことが述べられている
    • 「リファクタリング」は、技術的負債を継続的なリライトにより精算していこうとするソフトウェアテクノロジであり、このためには回帰テストが必須であるということが述べられている
      • 回帰テストとはデグレーションを防ぐために必要であり、つまり、リライトの前にテストが書かれなければならないという考えである
  • ユニットテストのテストスイートの初めての実装はケント・ベックによるSmalltalkのSUnitである
    • これはJavaのJUnitやRubyのTest::Unitといった後発のテストスイートの参考になっている
      • JUnitもケント・ベックが関わっている
      • setupとかteardownとかはここから来ている
      • こういう派生は「xUnit」と呼ばれている
  • 「spec」は新しいテストスイートの流れであり、どうもRuby界隈で人気らしい
    • テスト駆動開発から派生した「ビヘイビア駆動開発」で使われているテストスイートである
    • xUnitのテストコードは単なるコードでありそこから仕様を把握することが難しいという懸念があり、specは本物の動く仕様書となることを目指している

テストファーストな開発

  • テストを書くときに推奨されるのはレッド・グリーンである
    • コードの正当性を確かめるためにテストコードが必要であるという主張は分かる
      • しかし、テストコードの正当性はどうやって確かめればよいのだろうか
      • テストコードのテストコードまで書いていては切りがない
    • そのためにはまずテストを初めに失敗させ、そのあとグリーンにする
      • こうすればテストコードが正しく該当のコードを走らせていることが分かる
      • レッド・グリーンにならないなら、走らせている部分が間違っているか、テストコードの中身が間違っているかのどちらかである
    • 対象コードがなく新規に実装するならまずテストを書き、既存のコードに回帰テストを書くときは、既存のコードを書き換えて失敗させてみるのを試すのが良い
  • テストファーストで開発するかに関わらず、コードを書く際に、このコードはどうやってテストするかを考える習慣は有用である
    • テストはインターフェースを介して行われるので、実装ではなくインターフェースに注意が注がれるようになる
    • よく言われるのはテストコードを一緒に書くことによって設計が洗練されるということである

価値のあるテスト

  • テストコードには様々なメリットが各所で紹介されているが、共通することは、テストが最終的に開発のコストの増加を防ぐということである
  • 全てを網羅する必要はない
    • テストは少ないほど費用対効果が高く、網羅性を高めるほどリターンは少なくなっていく
    • 一般に90%以上のカバレッジは十分であると言われ、75%以下のカバレッジは足りないと言われている
  • 問題が起こりそうな箇所をテストするのである
    • コードが意図通り動くと言う安心感を得られるとともに、テストを書くチームは品質的なイニシアティブを得る事ができる
    • 明らかに間違いが無いと思われるアクセッサなどはテストする必要はない
  • 一般的にテクニックというものはコストに見合うメリットがなければ行う必要がない

再利用可能なコードとテスト

  • 良いコードの定義は人によって差があるだろうが、アジャイル開発においては、まず前提としてコードはテストの書きやすいものになっていなければならない
    • テストを考慮していないコードはテストを書くのが困難である
  • テストは一番始めのコードの再利用になる訳で、テスト可能なコードはテスト以外でも容易に再利用可能である
    • 再利用可能なコードはテストも書きやすく、またアジャイル的な開発プロセスを適応しやすい
    • テストコードを書くにしても、アジャイルを採用するにしても、再利用可能なコードを書くことを学ばなければならない
  • コードが単なる手続きであれば、テストを書くのは容易い
    • 単に入力と出力が一致していることを確認すれば良い
  • コードが他のオブジェクトに依存しているとき、テストを書くのは難しい
    • 対象コードの初期化のためにコンテキストが要求されるからだ
      • つまり、依存する全てのオブジェクトの初期化も必要になってくる
    • インターフェースによるオブエジェクト間のメッセージ送信が「依存」と呼ばれるものである
    • オブジェクト間の依存関係を理解することは、再利用可能でテスタブルなコードを書くポイントである
  • 優れたコードの書き方はマーチン・ファウラーの「リファクタリング」が詳しい
    • 因みに、コードの品質はある程度形式的にその品質を数値化出来ることが知られていて、色々な静的解析ツールが開発されている
      • コードの品質を示す値を「メトリクス」という
      • 昔はプログラマの生産性をコードの行数で判断してたそうだが、これは品質を示す指標にはならない
    • reekなどの静的なコードチェッカはメトリクスの計算にこの本で示されているチェック項目(smell)を採用している

オブジェクトの依存関係

  • 結局のところ依存とは他のオブジェクトへのメッセージの移譲により生まれる
  • オブジェクトがメッセージを受け取ったとき、それを処理するために、オブジェクトが取る行動は、二種類あることを理解しよう
    • 一つ目はそのメッセージを自分自身で処理して任務をまっとうする場合である
      • つまり一つのメソッドだけで計算が完結するか、selfのメソッドだけで計算を完了する
      • この時、このオブジェクトは他のオブジェクトに依存していない
    • 二つ目はそのメッセージを他のオブジェクトに送ることにより処理を「委譲」する場合である
      • つまり、移譲されたオブジェクトが送り手の代わりにメッセージを処理する
      • この時、送り手のオブジェクトは送り先のオブジェクトに依存している
  • 移譲先のオブジェクトは何種類かある
    • 例えば、継承されたオブジェクトはメッセージが処理できないとき、それを親クラスに移譲する
      • この時、メソッド名は移譲先と一致する
    • 他の例としては、他のオブジェクトを保持するオブジェクトは、メッセージを処理できないとき、保持するオブジェクトにメッセージを移譲する
      • 例えば、Bicycleオブジェクトにタイヤサイズを尋ねると、そのオブジェクトは自分が持っているTireオブジェクトにサイズを尋ねるという感じである
      • こいういうオブジェクトを「コンポジション」という

ダブルとモックの違い

  • コードをテストするときに障害になるのは、そのコードが他のオブジェクトに依存していて、初期化が困難であるときである
    • こういうときは、依存先のオブジェクトを別のオブジェクトに入れ替えてしまうことによって切り離し(スタブし)、対象コードのみを初期化するだけで楽にテストを動かせる
    • こういう代役となるオブジェクトは「ダブル」と呼ばれている
      • 映画の撮影の際に、危ない演技をさせるときはスタントマン(stunt double)を雇って、その人に演じてもらうような感じである
      • この時、その代役がどれだけ、本物と似ているべきかは要求されるシュチュエーションに依存している
  • オブジェクトの入れ替えには「依存性注入」を使う
    • 動的なオブジェクト指向言語では、「オープンクラス」して依存性注入を使わずにスタブする方法もある
      • TimeCopとかWebmockとか便利なので調べてみると良い
  • 依存するオブジェクトへ送られるメッセージは二種類しかないことを理解しておけばダブルの使い所が分かる
    • 一つは「クエリ」と呼ばれるもの
      • 送り手が返り値を期待してそれを使う
      • 送り手以外は返り値を受け取らないし、呼ばれたかどうかも意識しない
        • 例えば、設定ファイルをリードするオブジェクトがそれである
      • クエリを受け取るために作られたダブルを「スタブ」という
        • こいつはテスト用の値を返すだけのものである
    • 二つ目は「コマンド」と呼ばれるもの
      • 送り手が返り値は気にせず、全体への副作用を期待する
      • たとえば、データベースの更新とか、画面の書き換えとかがそれである
        • コマンドを受け取るために作られたダブルを「モック」という
          • こいつはどのようなメッセージを受け取ったかを記録していて、問い合わせることによって、正しくメッセージが送れたことを検証できるものである
          • 「エクスペクテイション」を予めプログラムしておいて、それ通りの呼び出しが行われたかを検証するのである
          • 色々な汎用モックが開発されていて、大抵のモックはMock#verifyとかで検証できるようになっている
  • ダブルの分類はGerard Meszarosの用語集が詳しい。以下の5つの分類が紹介されている
    • ダミーオブジェクト
    • テストスタブ
    • テストスパイ
    • モックオブジェクト
    • フェイクオブジェクト

参考文献