モック

モックには、Mockall というライブラリが広く使用されています。トレイトを使用するようにコードをリファクタリングする必要があります。これにより、すぐにモックできるようになります。

use std::time::Duration;

#[mockall::automock]
pub trait Pet {
    fn is_hungry(&self, since_last_meal: Duration) -> bool;
}

#[test]
fn test_robot_dog() {
    let mut mock_dog = MockPet::new();
    mock_dog.expect_is_hungry().return_const(true);
    assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true);
}
This slide should take about 5 minutes.
  • Mockall is the recommended mocking library in Android (AOSP). There are other mocking libraries available on crates.io, in particular in the area of mocking HTTP services. The other mocking libraries work in a similar fashion as Mockall, meaning that they make it easy to get a mock implementation of a given trait.

  • モックを使用する際は少し注意が必要です。モックを使用すると、テストを依存関係から完全に分離できます。その結果、より高速で安定したテスト実行が可能になります。一方、モックが誤って構成され、実際の依存関係の動作とは異なる出力が返される可能性があります。

    可能な限り、実際の依存関係を使用することをおすすめします。たとえば、多くのデータベースではインメモリ バックエンドを構成できます。つまり、テストで正しい動作が得られ、しかも高速で、テスト後は自動的にクリーンアップされます。

    同様に、多くのウェブ フレームワークでは、localhost 上のランダムなポートにバインドするプロセス内サーバーを起動できます。このような構成は実際の環境でコードをテストすることを可能にするので、フレームワークをモックすることよりも常に優先して利用しましょう。

  • Mockall は Rust プレイグラウンドの一部ではないため、この例はローカル環境で実行する必要があります。cargo add mockall を使用して、Mockall を既存の Cargo プロジェクトにすばやく追加します。

  • Mockall にはさらに多くの機能があります。特に、渡される引数に応じて期待値を設定できます。ここでは、最後に餌を与えてらえてから 3 時間後に空腹になる猫をモックするためにこれを使用します。

#[test]
fn test_robot_cat() {
    let mut mock_cat = MockPet::new();
    mock_cat
        .expect_is_hungry()
        .with(mockall::predicate::gt(Duration::from_secs(3 * 3600)))
        .return_const(true);
    mock_cat.expect_is_hungry().return_const(false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true);
}
  • .times(n) を使用すると、モックメソッドが呼び出される回数をn に制限できます。これが満たされない場合、モックはドロップ時に自動的にパニックになります。