第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を開催した。
インテグレーションテストとユニットテストの違いとか使い分けとか、テストの基本的な部分がやっぱり難しい気がする...。もう少しこの辺勉強しないとな。
今週のプルリクエストはこちら。