编辑:
下面的测试助手现在可以在 dedicated crate
中使用
免责声明:我是合著者
我也有同样的需求,并实现了一些小型测试助手来处理@Shepmaster 提到的注意事项。
这些测试助手支持这样的测试:
#[test]
fn test_default_log_level_is_info() {
with_env_vars(
vec![
("LOGLEVEL", None),
("SOME_OTHER_VAR", Some("foo"))
],
|| {
let actual = Config::new();
assert_eq!("INFO", actual.log_level);
},
);
}
with_env_vars 将负责:
- 在并行运行测试时避免副作用
- 在测试闭包完成时将 env 变量重置为其原始值
- 支持在测试关闭期间取消设置环境变量
- 以上所有,当测试关闭出现恐慌时也是如此。
帮手:
use lazy_static::lazy_static;
use std::env::VarError;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::Mutex;
use std::{env, panic};
lazy_static! {
static ref SERIAL_TEST: Mutex<()> = Default::default();
}
/// Sets environment variables to the given value for the duration of the closure.
/// Restores the previous values when the closure completes or panics, before unwinding the panic.
pub fn with_env_vars<F>(kvs: Vec<(&str, Option<&str>)>, closure: F)
where
F: Fn() + UnwindSafe + RefUnwindSafe,
{
let guard = SERIAL_TEST.lock().unwrap();
let mut old_kvs: Vec<(&str, Result<String, VarError>)> = Vec::new();
for (k, v) in kvs {
let old_v = env::var(k);
old_kvs.push((k, old_v));
match v {
None => env::remove_var(k),
Some(v) => env::set_var(k, v),
}
}
match panic::catch_unwind(|| {
closure();
}) {
Ok(_) => {
for (k, v) in old_kvs {
reset_env(k, v);
}
}
Err(err) => {
for (k, v) in old_kvs {
reset_env(k, v);
}
drop(guard);
panic::resume_unwind(err);
}
};
}
fn reset_env(k: &str, old: Result<String, VarError>) {
if let Ok(v) = old {
env::set_var(k, v);
} else {
env::remove_var(k);
}
}