Slide 1

Slide 1 text

© 2024 Finatext Holdings Ltd. Mocking in Rust Applications Taiki Ono, Finatext

Slide 2

Slide 2 text

© 2024 Finatext Holdings Ltd. Taiki Ono @taiki45 1

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

© 2024 Finatext Holdings Ltd. 1. モックするまでの道のり 2. モックを定義する・使う 例として使ったコードは https://github.com/Finatext/orgu に全貌あります 今回の話は単体テストくらいの粒度のテストをスコープにしています 目次 3

Slide 5

Slide 5 text

© 2024 Finatext Holdings Ltd. モックするまでの道のり 4

Slide 6

Slide 6 text

© 2024 Finatext Holdings Ltd. テスト対象のロジックは外部依存について差し替え可能であることが望ましいです(後述) Rustでは交換可能性はGenericsまたはTrait Objectsを使って実現できます ● Generics (+ Trait Bounds): コンパイル時に具体的な型が分かるケースで利用します。コンパイラがそれぞ れの具体的な型用のコードを生成する。静的ディスパッチ ● Trait Objects: コンパイル時に具体的な型が分からないケースで利用します。実行時のメソッド呼び出し時 に仮想関数テーブルを参照して実際の呼び出し関数(メソッド)を決定します。動的ディスパッチ 交換可能性を導入する 5

Slide 7

Slide 7 text

© 2024 Finatext Holdings Ltd. 非同期ジョブのロジックという例: 「GitHub Checks APIを使ってCheckRunを作成して、Git cloneでリポジトリをクローンして、指定された外部 コマンドを実行する」 テスト時にモックしたい部分 ● GitHub APIを呼ぶ: GithubClient trait ● Git clone: Checkout trait →このケースでは例えば、Handlerという構造体を定義してそれぞれの外部依存を持たせる 交換可能性の例 6

Slide 8

Slide 8 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: GithubClient trait 7

Slide 9

Slide 9 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: Checkout trait 8

Slide 10

Slide 10 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: Handler 9

Slide 11

Slide 11 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: Handlerの初期化関数 10

Slide 12

Slide 12 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: ドメインロジック 11

Slide 13

Slide 13 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: 初期化 12

Slide 14

Slide 14 text

© 2024 Finatext Holdings Ltd. 静的ディスパッチのアプローチ: テスト時の初期化 13

Slide 15

Slide 15 text

© 2024 Finatext Holdings Ltd. 動的ディスパッチのアプローチ: Handler定義 14 ※動的ディスパッチ

Slide 16

Slide 16 text

© 2024 Finatext Holdings Ltd. 動的ディスパッチのアプローチ: 初期化 15

Slide 17

Slide 17 text

© 2024 Finatext Holdings Ltd. ケースバイケース Webアプリケーションの場合はIOの方がはるかにでかいので、生産性その他で決めるといいです 静的ディスパッチ vs 動的ディスパッチ 16

Slide 18

Slide 18 text

© 2024 Finatext Holdings Ltd. axum crateのStateのような仕組みを使う http crateのRequest構造体にはexstensionの仕組みがあるので、このexstensionに状態を毎回セットする tower::Serviceを作ると同じようなことが実現できる(はず) ある程度の規模以上だと依存管理が煩雑になりそうなのでDependency Injdectionライブラリが有効そうだが少な い。DIパターンもデファクトがあるわけではなさそうなので今後の発展に期待(っぽい) Webアプリケーションでの交換可能性の実現 17

Slide 19

Slide 19 text

© 2024 Finatext Holdings Ltd. Webアプリケーションでの交換可能性の実現 18

Slide 20

Slide 20 text

© 2024 Finatext Holdings Ltd. モックを定義する・使う 19

Slide 21

Slide 21 text

© 2024 Finatext Holdings Ltd. ● #[automock] ○ 先の例のような典型的なケースではこのattribute macroで自動生成する ● mock! ○ #[automock]マクロではダメなケースでこのproc macroで半手動(?)で生成する 名前に "Mock" prefixが付いた構造体が定義されるので、::newか::defaultで構造体を初期化する モック構造体の専用のメソッドを使ってモック対象のtraitのメソッド呼び出しのexpectationを設定する Expectation設定用のメソッド名は "expect_" という規約 デストラクタで設定されたexpectationを検証する mockall crate 20

Slide 22

Slide 22 text

© 2024 Finatext Holdings Ltd. 例: モック定義 21

Slide 23

Slide 23 text

© 2024 Finatext Holdings Ltd. 例: モックを使う 22

Slide 24

Slide 24 text

© 2024 Finatext Holdings Ltd. 例: メソッドの引数の検証 23

Slide 25

Slide 25 text

© 2024 Finatext Holdings Ltd. ● 構造体の実装のモックはできるが名前空間上でハック(?)が必要でやや微妙 ○ 後述 ● Associated functions(static methods?)のモックはexpectationの設定がグローバルになるので、テスト の並列実行で同期ロジックが必要 ○ なるべく避けたい ● モジュール上の関数もモック可能だが名前空間上のハックが必要、かつexpectationの設定がグローバルにな る ○ なるべく避けたい ● モック対象のtraitのメソッドが参照を返す場合は制限あり ● モック対象のtraitのメソッドの返り値がimpl Traitの場合は、mockall内部でBoxに変換するの で制限あり ● モック対象のtraitのメソッドの返り値がimpl Futureの場合は、返り値をpinningする必要あり ○ 実際はasyncキーワードとasync-trait crateを使うことが多い(?) ○ 後述 ドキュメントとexamplesがかなり丁寧でその他のケースも網羅してるので困った時はどうぞ mockallの注意点 24

Slide 26

Slide 26 text

© 2024 Finatext Holdings Ltd. async_traitマクロの手前にautomockマクロを置く。言い換えると、async_traitマクロで書き換えられた後の コードに対してautomockマクロでモック生成する async_traitマクロがその名前でimportされている必要あり。内部で使っているっぽい async-trait crateとの併用 25

Slide 27

Slide 27 text

© 2024 Finatext Holdings Ltd. 構造体の実装のモック 26

Slide 28

Slide 28 text

© 2024 Finatext Holdings Ltd. Associated Functionsの交換 27

Slide 29

Slide 29 text

© 2024 Finatext Holdings Ltd. 実用Rustアプリケーション開発 28 https://zenn.dev/taiki45/books/pragmatic-rust-application-development

Slide 30

Slide 30 text

No content