new関数を含む以下のようなトレイトをmockallでモックするのに手間取ったので、その手順をメモする
trait Foo {
fn new(x: u32) -> Self;
}
手順
コード全体としては以下のようになる。
#[automock]
trait Foo {
fn new(x: u32) -> Self;
}
#[test]
fn mock_new() {
let foo = MockFoo::default();
let foo_ctx = MockFoo::new_context();
foo_ctx.expect()
.return_once(|_| foo);
}
手順を分解して説明すると、以下の1. ~ 3.の通りとなる
1. モックの実装
モックの実装手順はいつもの手順と同じ
#[automock]
trait Foo {
fn new(x: u32) -> Self;
}
2. モックの実体化
モックの実体化は以下のどちらかで行う
// デフォルト値で実体化
let foo = MockFoo::default();
// 引数を与えて実体化
let foo = MockFoo::new(1);
-
2番目の方法について、いつもの手順で使われる引数なしのnew関数(=
MockFoo::new()
)は実装されず、以下のようなエラーが出るので注意する| 2 | let foo = MockFoo::new(); | ^^^^^^^^^^^^-- an argument of type `u32` is missing |
3. new関数の期待する呼び出しを定義する
new関数に対応するコンテキスト?を作成して、そのコンテキストで期待する呼び出しを実装するようにする
let foo_ctx = MockFoo::new_context();
// expect()以降のreturn_onceなどについては、return_constなど他のいつものやつも使えるはず
foo_ctx.expect()
.return_once(|_| foo);
- いつものように
expect_new()
とするのではなく、単にexpect()
とする - コンテキスト作成の際に、newを指定している理解(
new_context()
)
一般的な話 : 静的メソッドのモック
上記手順は、静的メソッド(=self
を含まないメソッド)をモックする場合も同様となる
#[automock]
trait Foo {
fn bar(x: u32) -> u32;
}
#[test]
fn mock_bar() {
// [関数名]_context()で静的メソッドのコンテキストを作成できる
let foo_bar_ctx = MockFoo::bar_context();
foo_bar_ctx.expect()
.return_const(1u32);
}
+ε : いつものモックと静的メソッドのモックを組み合わせる
いつものモック=静的じゃないメソッドのモック(と思っている)
#[automock]
trait Foo {
// new関数=静的メソッド
fn new(x: u32) -> Self;
// いつものほう
fn bar(&self) -> u32;
}
#[test]
fn mock_new_bar() {
// MockFoo::new(1)でもよいと思われる
let mut foo = MockFoo::default();
let foo_ctx = MockFoo::new_context();
// 二つ目の文でfooの所有権が移動するので先にbarの期待する呼び出しを定義する
// (他にいい方法があるかもしれない、、、)
foo.expect_bar()
.return_const(1u32);
foo_ctx.expect()
.return_once(|_| foo);
}
参考
- ドキュメント
- 関連してそうなソースコード
- いつもの
MockFoo::new()
が実装されない理由