開発日報

窓際エンジニアの開発備忘。日報は嘘です。

テストダブルの原則 ② ~テストダブル利用のテクニック~

テストダブル利用のテクニック

モッキングフレームワーク

モッキングフレームワークはオブジェクトをモック(mock)と置き換えられるようにする。

注意点として使いすぎるとコードの保守が難しくなる点が挙げられる。

// 例)モッキングフレームワーク

class PaymentProcessorTest {
  ......
  PaymentProcessor paymentProcessor;

  // CreditCardServiceのテストダブルをコード1行だけで作成する。 
  @Mock CreditCardService mockCreditService;
  
  @Before public void setUp() {
    // テスト対象システムにテストダブルを渡す。
    paymentProcessor = new PaymentProcessor(mockCreditService);
  }

  @Test public void chargeCreditCardFails_returnFalse() {
    // テストダブルの挙動を定義する
    // どんな引数を与えて呼び出してもfalseが返却される
    when(mockCreditCardService.chargeCreditCard(any(), any()))
      .thenReturn(false);
    boolean success = paymentProcessor.makePayment(CREDIT_CARD, AMOUNT);
    assertThat(success).isFalse();
  }
}

フェイキング

  • フェイク(fake):本番環境には適さないが、本物の実装同様に振る舞うAPIの軽量実装。
    • 例) メモリ内DB

注意点として、フェイクは現在・将来において本物の実装同様の挙動を保つことを担保しなければならない

// フェイクの作成は高速で容易である
AuthorizationService fakeAuthorizationService = new FakeAuthorizationService();
AccessManager accessManager = new AccessManager(fakeAuthorizationService );

// 不明なユーザーIDはアクセス権を持つべきでない
assertFalse(accessManager.userHasAccess(USER_ID));

// ユーザーIDは、認証サービスへ追加された後はアクセス権を持つべきである
fakeAuthorizationService.addAuthoraizedUser(new User(USER_ID));
assertThat(accessManager.userHasAccess(USER_ID)).isTrue();

スタビング

それ自体では何も挙動を持たない関数に挙動を与えるプロセス。つまり、関数が返すべき正確な値を関数に対して指定すること。指定した返り値を(スタブ)という。

// モッキングフレームワークにより生成されたテストダブルを渡す。
AccessManager accessManager = new AccessManager(mockAuthorizationService);

// nullが返されるユーザーIDはアクセス権を持つべきでない
when(mockAuthorizationService.lookupUser(USER_ID)).thenReturn(null);
assertThat(accessManager.userHasAccess(USER_ID)).isFalse();

when(mockAuthorizationService.lookupUser(USER_ID)).thenReturn(USER);
assertThat(accessManager.userHasAccess(USER_ID)).isTrue();

インタラクションテスト

関数がどのように呼び出されるかを実際に対象の関数の実装を呼び出すことなく検証する手法。

インタラクションテストはテスト対象の内部構造に強く依存するため使いすぎると脆いテストに陥るため、可能なら避けるべき。

AccessManager accessManager = new AccessManager(mockAuthorizationService);
accessManager.userHasAccess(USER_ID);

// verify メソッドがlookupUserメソッドを期待通りに呼び出しているかを検証
verify(mockAuthorizationService).lookupUser(USER_ID);

本物の実装

テストダブルは非常に有用だが、Googleにおいては本物の実装(本番環境向けコードで利用されている実装)をテストに利用することを最優先としている。

モッキングフレームワークを使いすぎると、本物の実装とテストコードの同期がとりにくくなり、リファクタリングが難しくなり、テストが汚染されることになる(詳細は次回の記事にて)。