第77回です。前回はこちら。
[第77回の様子]
2023/05/10に第77回を開催した。GW中はお休みだったので2週間ぶり。
内容としてはRust By Example 日本語版21. テストの「21.2. ドキュメンテーションテスト」、「21.3. インテグレーションテスト」、「21.4. 開発中の依存関係」に取り組んだ。
参加者は自分を入れて6人。初参加のメンバーも来てくれた!
[学んだこと]
- 21.2. ドキュメンテーションテスト
- Rustではコードのドキュメント内にコードブロックをおくことで、テストとして利用できる
- コードブロックは暗黙的に
fn main() { ... }
で囲まれており、テスト対象の関数はdoccomentsクレートとして呼び出すことができる
/// ``` /// let result = doccomments::add(2, 3); /// assert_eq!(result, 5); /// ``` pub fn add(a: i32, b: i32) -> i32 { a + b } // コメント中のコードブロックはcargo testでdoc-testsとして実行される $ cargo test running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests doccomments running 1 tests test src/lib.rs - add (line 7) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- パニックする結果のテストの場合はコードブロックにshould_panicをつけるとテストが通る
/// ```rust,should_panic /// doccomments::div(10, 0); /// ``` pub fn div(a: i32, b: i32) -> i32 { if b == 0 { panic!("Divide-by-zero error"); } a / b }
- ドキュメンテーションテストではコードブロックの周囲に
fn main() { ... }
があるので、戻り値がユニット()
でなければならない - 以下のように
#
でドキュメントからコードを隠すことで、テスト中に?
演算子を利用できる
/// ``` /// # // 次の行はコンパイル時にのみ利用される /// # fn try_main() -> Result<(), String> { /// let res = doccomments::try_div(10, 2)?; /// # Ok(()) /// # } /// # fn main() { // 明示的にmain関数を配置 /// # try_main().unwrap(); /// # } /// ``` pub fn try_div(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err(String::from("Divide-by-zero")) } else { Ok(a / b) } }
- 21.3. インテグレーションテスト
- cargoではsrcディレクトリの横に置かれたtestsディレクトリをインテグレーションテストとして扱う
// src/lib.rs // adderクレートの関数 pub fn add(a: i32, b: i32) -> i32 { a + b } // tests/integration_test.rs // テスト対象のクレートをexternで宣言 extern crate adder; #[test] fn test_add() { assert_eq!(adder::add(3, 2), 5); } // cargo testでインテグレーションテストを実行できる $ cargo test running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/integration_test-bcd60824f5fbfe19 running 1 test test test_add ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests adder running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
- インテグレーションテスト間でコードを共有するには共通のモジュールに関数を定義する
// tests/common.rs pub fn setup() { // テストに必要なファイル・ディレクトリの作成やサーバの起動といった準備を行うコードを記述する。 } // tests/integration_test.rs // テスト対象のクレートをexternで宣言 extern crate adder; // 共通のモジュールをインポート mod common; #[test] fn test_add() { common::setup(); assert_eq!(adder::add(3, 2), 5); }
- 21.4. 開発中の依存関係
- テストのための依存関係は、Cargo.tomlの[dev-dependencies]セクションで定義する
- 例えば、テストで利用するpretty_assertionsクレートがある
// Cargo.toml # 関係のない行は省略 # 1はクレートのバージョン [dev-dependencies] pretty_assertions = "1"
- テストコードで利用する場合は次のようにする
// テストにのみ使うクレートをexternで宣言 #[cfg(test)] #[macro_use] extern crate pretty_assertions; // テスト対象の関数 pub fn add(a: i32, b: i32) -> i32 { a + b } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn test_add() { assert_eq!(add(2, 3), 5); } }
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
インテグレーションテストとユニットテストの違いとか使い分けとか、テストの基本的な部分がやっぱり難しい気がする...。もう少しこの辺勉強しないとな。
今週のプルリクエストはこちら。