Rustのmockallクレートを利用してみた。
Rustでモッククレートはいくつかあるが、一番良さそうであったmockallクレートを利用してみた。
https://crates.io/crates/mockall
今回確認した各種バージョン情報
名称 | バージョン |
---|---|
Windows10 Pro | 1909 |
Rust | 1.47.0 |
mockall | 0.8.3 |
automockアトリビュートが何をしているのかわからないので、Usageにあるトレイトをcargo expandしてみる
#[cfg(test)] use mockall::{automock, mock, predicate::*}; #[cfg_attr(test, automock)] trait MyTrait { fn foo(&self, x: u32) -> u32; }
cargo expand --lib --tests
結果
#[cfg(test)] use mockall::{automock, mock, predicate::*}; trait MyTrait { fn foo(&self, x: u32) -> u32; } #[allow(non_snake_case)] #[allow(missing_docs)] pub mod __mock_MockMyTrait { use super::*; } #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(missing_docs)] struct MockMyTrait { MyTrait_expectations: MockMyTrait_MyTrait, } impl ::std::default::Default for MockMyTrait { #[allow(clippy::default_trait_access)] fn default() -> Self { Self { MyTrait_expectations: Default::default(), } } } #[allow(non_snake_case)] #[allow(missing_docs)] pub mod __mock_MockMyTrait_MyTrait { // 長いので省略 } #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(missing_docs)] struct MockMyTrait_MyTrait { foo: __mock_MockMyTrait_MyTrait::__foo::Expectations, } impl ::std::default::Default for MockMyTrait_MyTrait { fn default() -> Self { Self { foo: Default::default(), } } } impl MockMyTrait_MyTrait { /// Validate that all current expectations for all methods have /// been satisfied, and discard them. pub fn checkpoint(&mut self) { { self.foo.checkpoint(); } } } impl MockMyTrait { /// Validate that all current expectations for all methods have /// been satisfied, and discard them. pub fn checkpoint(&mut self) { self.MyTrait_expectations.checkpoint(); } /// Create a new mock object with no expectations. /// /// This method will not be generated if the real struct /// already has a `new` method. However, it *will* be /// generated if the struct implements a trait with a `new` /// method. The trait's `new` method can still be called /// like `<MockX as TraitY>::new` pub fn new() -> Self { Self::default() } } impl MyTrait for MockMyTrait { fn foo(&self, x: u32) -> u32 { self.MyTrait_expectations .foo .call(x) .expect("MockMyTrait::foo: No matching expectation found") } } impl MockMyTrait { #[must_use = "Must set return value when not using the \"nightly\" feature"] ///Create an [`Expectation`](__mock_MockMyTrait_MyTrait/__foo/struct.Expectation.html) for mocking the `foo` method fn expect_foo(&mut self) -> &mut __mock_MockMyTrait_MyTrait::__foo::Expectation { self.MyTrait_expectations.foo.expect() } }
トレイト名に接頭辞Mockを付けた構造体とそれに加えて接尾辞__トレイト名
の構造体を実装するようだ。
トレイトメソッドはexpect_メソッド名
のメソッドが実装されこれがモックメソッドになるようだ。
基本的には、複数の実装(impl)をする場合は、mockマクロを使い、一つだけの実装であれば、automock属性を使うのが良さそう。
reqwest利用箇所をモック化してみた。
基本的にはautomockアトリビュートかmockマクロを使うだけで非常に簡単に実装できる。