2021/10/19
rustで単体テストをSerialに(同一スレッドで)実行する
rustでテストケースを複数実行すると、意図せずエラーになる場合があります。
これは複数のテストケースが同時に実行されるからで、例えば環境変数を使うような場合や、同一のデータベースを使うような場合など
スレッドを跨いで同じリソースを使うようなテストケースをまとめて実行するケースでエラーになってしまいます。
これを解決するのが serial_test crateです。(すごい便利でした。)
テストの関数に#[serial]
macroを宣言すれば良いだけで使えるのが、とても便利でした。
詳細な使い方は以下のようになります。(コメントでインラインに記載しています)
how to install
[dev-dependencies]
serial_test = "0.5.1" # この行を追加
how to use
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config {
pub host: String,
pub project_id: String,
pub project_key: String,
pub environment: Option<String>,
pub app_version: Option<String>,
pub user_agent: String,
}
fn default_user_agent() -> String {
format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
}
impl Default for Config {
fn default() -> Self {
let host = std::env::var("AIRBRAKE_HOST").unwrap_or("https://api.airbrake.io".to_owned());
let project_id = std::env::var("AIRBRAKE_PROJECT_ID").unwrap_or("0".to_owned());
let project_key = std::env::var("AIRBRAKE_API_KEY").unwrap_or("0".to_owned());
let environment = std::env::var("AIRBRAKE_ENVIRONMENT").map(|inner| Some(inner)).unwrap_or(None);
Self {
host,
project_id,
project_key,
environment,
app_version: None,
user_agent: default_user_agent(),
}
}
}
impl Config {
pub fn endpoint(&self) -> String {
format!(
"{}/api/v3/projects/{}/notices",
self.host,
self.project_id,
)
}
}
#[cfg(test)]
mod tests {
use super::Config;
use serial_test::serial; // ★★★ ここ
#[test]
#[serial] // ★★★ ここ
fn test_default_config() {
std::env::remove_var("AIRBRAKE_HOST");
std::env::remove_var("AIRBRAKE_PROJECT_ID");
std::env::remove_var("AIRBRAKE_API_KEY");
std::env::remove_var("AIRBRAKE_ENVIRONMENT");
let config = Config::default();
let expected = Config {
host: "https://api.airbrake.io".to_owned(),
project_id: "0".to_owned(),
project_key: "0".to_owned(),
environment: None,
app_version: None,
user_agent: "errbit-0.1.0".to_owned(),
};
assert_eq!(expected, config);
assert_eq!(
"https://api.airbrake.io/api/v3/projects/0/notices",
config.endpoint()
);
}
#[test]
#[serial] // ★★★ ここ
fn test_config() {
std::env::set_var("AIRBRAKE_HOST", "https://errbit.example.com");
std::env::set_var("AIRBRAKE_PROJECT_ID", "1");
std::env::set_var("AIRBRAKE_API_KEY", "my-key");
std::env::set_var("AIRBRAKE_ENVIRONMENT", "dev");
let config = Config::default();
let expected = Config {
host: "https://errbit.example.com".to_owned(),
project_id: "1".to_owned(),
project_key: "my-key".to_owned(),
environment: Some("dev".to_owned()),
app_version: None,
user_agent: "errbit-0.1.0".to_owned(),
};
assert_eq!(expected, config);
assert_eq!(
"https://errbit.example.com/api/v3/projects/1/notices",
config.endpoint()
);
}
}
関連する記事
Concordiumノードをローカルで動かしてみた
Concordiumの調査のために、ローカルでソースコードをビルドしてノードを動かしてみました
[Rust]axumとdragonflyを使ったWebsocket Chatのサンプル実装
redis互換のdragonflyをPUBSUBとして利用して、Websocket Chatアプリのサンプル実装を行いました。
[Rust]TiDBを使ったサンプルアプリケーションの実装
RustからTiDBを使ったアプリケーションの実装を行いました。
[Rust]Google Cloud Storageを利用する
GCSやNFSのファイルを扱えるpackageをRustで実装しました。