Skip to content

Instantly share code, notes, and snippets.

@wm3
Last active December 16, 2015 04:59
Show Gist options
  • Save wm3/5381536 to your computer and use it in GitHub Desktop.
Save wm3/5381536 to your computer and use it in GitHub Desktop.
24-25章.md
  • 今までの説明 → テストの可読性、 有用な診断メッセージ
  • 今回 → 関係あるコードが壊れてる時だけ失敗させる

この章の趣旨を一言で表すと…

「何が起こるべきなのかを正確に指定せよ。余計なことを一切指定するな」

情報の表現ではなく中身をテストする

例: 顧客を検索するためのクラスのモック

null を返してみる → 不正確

  • 問題1:「null」が何を意味するのかが分からない
  • 問題2: リファクタリングに弱い

解決策: カスタマーが見つからなかった事を指す定数を用意する

的確なアサーション

シナリオに関連する内容だけチェックする。他はチェックしない。

  • 入力と無関係な物
  • 他のテストでカバーされている物

返される値が複雑な場合

  • 関心のある属性をテストする
  • 文字列の場合は興味のある文字列が含まれているかをチェックする
  • 構造化されたオブジェクトが必要だというサイン

的確なエクスペクテーション

  • 的確にコードを書く事はエクスペクテーションの部分にも当てはまる
  • (半分は jMock の機能紹介)

的確なパラメータマッチング

  • 例: sniperForItem カスタムマッチャーを引数に使用

アローアンスとエクスペクテーション

  • 例: ignoring() と allowing()
  • 呼ばれなくても良いという事を示唆する
  • 呼ばれた回数を指定する事も出来る(atLeast())
  • コラム: クエリにはアローアンス、コマンドにはエクスペクテーション
    • コマンド: 副作用がある、何かが変更されることが期待される
    • クエリ: 副作用が無い、つまり不要なら呼び出されなくても良い

無関係なオブジェクトは無視する

  • jMock はゼロを表すオブジェクトを用意している
  • 立て続けにignoring()を使っている場合は新しいオブジェクトとして切り出せるサイン

起動の順序

  • jMock は起動順序は基本的に気にしない
    • 順序が必要でないのなら気にするべきでも無い
    • パラメータのマッチングに使うので全く気にしない訳ではない
  • 起動順をチェックする仕組み
    • Sequence / inSequence() … 単純に順序を指定する
    • States … ステートマシン
  • あまりに複雑な起動順チェックは、クラスを分解するべきというサイン

jMock State の威力

  • State はテスト対象/ピア/テスト自身の論理的な状態

更に自由なエクスペクテーション

  • リフレクションを活用したエクスペクテーションも使える

「モルモット」オブジェクト

例: XmlMarshaller アダプター

  • テストに AuctionClosedEvent を使い回している

    • これは Xml の永続化には関係無いクラス
  • AuctionClosedEvent が使われない事になった場合

    • AuctionClosedEvent 自身が実際にここ以外で使われなくても残る
  • AuctionClosedEvent の構造が変化した場合

    • 一部のフィーチャーがテストでカバーされなくなる
  • 解決策: テスト用に専用のクラスを用意する

(感想)

  • 本当の問題は何が起こるべきなのかを正確に把握する事とその習慣だと思う。
    • これさえクリアすればそれをテストに落とし込むのはそう難しくない
    • でもその解決策は書いていない

永続

  • ORM や他の DB 抽象化レイヤーは裏に多くの処理を隠している
  • テストする項目が見た目より多い

永続状態に及ぼすテストを分離せよ

各テストが始まる時点でテーブル等のクリーンアップを済ませておく

Tips: 永続データの消去は、テストの最後でなく開始時に行おう

  • 失敗時のデバッグがしやすい

全てのテーブルを初期化する DatabaseCleaner を作る セットアップで呼び出す

テストのトランザクション境界を明示する

各テストのクリーンアップ時にロールバックする方法は推奨しない

  • コミットのテストが出来ない
  • 複数のトランザクションをまたがったテストも出来ない

トランザクション内の処理を内部クラスで表現する (Transactor / UnitofWork)

永続操作を行うオブジェクトのテスト

例: カスタマーの登録と検索

  • 登録のチェックにIDではなく名前を使用
    • 単純で、自己言及的なので
  • addCustomer()だけのテストは他の所では行わない
  • テストの方が実装より長いが、それは裏でJPAが設定を行っているため

コラム: パターンや型の名前について

  • CustomerBase はリポジトリ/DAO とも呼ばれる
  • あえてこの名前は使っていない
    • 下層のレイヤの知識を使っているため
    • パターンを名前に付けるのは良くない。それよりも他のクラスとの関係を書く
    • Data/Access/Object 等の一般的な名前を使わない

オブジェクトが永続化できる事のテスト

  • 全てのエンティティを往復させるテスト(ラウンドトリップテスト)が有効
  • 永続オブジェクトのラウンドトリップ
    • 各エンティティを保存して別のトランザクションで再取得
    • 全ての状態が一致しているかをチェック
  • 関連するエンティティのラウンドトリップ
    • ビルダーを組み合わせて関連するオブジェクトを構築する
    • 依存しているオブジェクトの保存が終わってから構築する

コラム: リフレクションの使用について

  • 公開APIを使ってテストするのが基本
  • 今回はプライベートなフィールドをリフレクションを使ってテストしている
    • コードではなく設定のテストをしているからOK

だけど、データベースのテストは、お・そ・いっ

効率的にフィードバックを得るためにユニットテスト/インテグレーションテスト/エンドツーエンドテストの三段階に分ける

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment