モックオブジェクトを作ることを通してRefCellやMutex、Rc、Arcの使い方やSendやSyncについて学びます。モックオブジェクトを自作できるようになります。最後に参考文献サンプルプログラムまず書いていきたいコードとして以下のようなcreate_user_with_nameというユースケースを考えます。名前が空文字でなければUserPort.storeを呼ぶというコードです。}}以下のように呼び出します。サイクルを回しながら設計と実装を進めていきます。したがって上のcreate_user_with_nameの実装はまだ私たちの頭の中にしかないものとして、まず成功系のテストを考えてみます。}}このような感じで良さそうです。最後にstore_callsを使ってuser_port.storeが正しい引数で呼ばれたことをverifyしています。いえUserPortMockをまだ作っていないのでまだ動きません。}}こんな感じでしょうか。上記の矢印のところでpushするためにmutableな参照が取れないのでコンパイルエラーになります。selfにすれば、問題は解決しますが、テストの都合でUserPortのインターフェースを変えたくありません。RefCellインターフェースは不変参照にしたいが可変参照が欲しいといったような上記のようなニーズに応えるのがRefCellです*2。RefCellを使うと不変参照しかないところに可変参照を生やすことができます。storeの型をVec<User>からRefCell<Vec<User>>に変え、push(user)の前にborrow_mut()することで先程の例のコンパイルが通るようになります。}]);これでUserUseCaseを実装すれば、ちゃんとテストが通る状態になりました。非同期もしuser_port.storeを非同期な処理にしたい場合はどうでしょうか。クレートを使って以下のような感じです。String>;}ユースケース側は単に関数をasyncにしてuser_port.storeの呼び出しの後に.awaitをつける予定で考えます。クレートなどのテスト用ランタイムを使って以下のような感じになります。*/}あとはUserPortMockの実装を変える必要がありますが、単に下のようにするだけではコンパイルエラーになります。参照(&self)を別スレッドに持っていくということはスレッド間で値を共有するということなのでUserPortMock(self)はSyncである(スレッド間で安全に値を共有できる)必要があります。RefCellはSyncトレイトを実装していないのでUserPortMock全体で!Syncとなり、&selfは!Sendになってしまいます。安全に&selfを別スレッドに移動できないのでコンパイルエラーです。そこでMutexを使います*4。MutexはRefCellと似たような使い方ができ、Syncが実装されています。RefCell<Vec<User>>をMutex<Vec<User>>に型を変えます。borrow_mut()やborrow()をlock()にします。);}これでコンパイルが通りちゃんとテストできるようになります。今回の例であるuser_port.storeでは若干違和感がありますが、&selfではなくselfな関数をモックしたいこともあります。ためしに、UserPortのstoreとUserUseCaseのcreate_user_with_nameの&selfから&を外してみると、問題が発生します。テストコード内でtarget.create_user_with_nameを呼び出すとtargetの所有権が奪われてtarget.user_port.store_callsを借用できなくなるからです。);}そこでRcやArcを使って複数の所有権を持てるようにします。非同期の例ではRcが!SendなのでArcを使ってMutex<Vec<User>>をArc<Mutex<Vec<User>>>に変えます。以下のように先にstore_callsをcloneしておき、それを使うことでコンパイルが通るようになります。);}最後にと、頑張ってモックオブジェクトを作りましたが、最近になってmock-itというクレートを見つけました。内部実装を見るとほとんど同じことをやっています。こちらを使うほうが便利で書く量も少なくて済むので、徐々にこちらに移行しているところです。reserved.引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。