我严格地尝试模仿std::path::PathBuf.is_dir
方法,但我认为这里有一个更通用的用例,实际上这是关于模拟外部功能。
我创建了一个trait来封装PathBuf.is_dir
方法,理论上,根据mockall文档,应该能够使我可以模拟我的is_dir
封装.
use mockall::*;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq)]
pub enum PackageFileIndexError {
ArchiveRootNotADirectory,
}
#[automock]
trait PathInterface {
// Encapsulate the is_dir method to make it mockable.
fn is_dir(this_path: &PathBuf) -> bool {
this_path.is_dir()
}
}
pub struct PackageFileIndexData {
archive_root_path: PathBuf,
}
impl PackageFileIndexData {
pub fn new(archive_root: &str) -> Result<PackageFileIndexData, PackageFileIndexError> {
let archive_root_path = PathBuf::from(archive_root.clone());
if !Self::is_dir(&archive_root_path) {
return Err(PackageFileIndexError::ArchiveRootNotADirectory);
}
Ok(PackageFileIndexData { archive_root_path })
}
}
impl PathInterface for PackageFileIndexData {}
#[cfg(test)]
mod tests {
use super::*;
mock! {
PackageFileIndexData {}
trait PathInterface {
fn is_dir(this_path: &PathBuf) -> bool;
}
}
#[test]
fn test_bad_directory() {
let ctx = MockPackageFileIndexData::is_dir_context();
ctx.expect().times(1).returning(|_x| false);
let result = PackageFileIndexData::new("bad_directory").err().unwrap();
assert_eq!(result, PackageFileIndexError::ArchiveRootNotADirectory);
}
#[test]
fn test_good_directory() {
let ctx = MockPackageFileIndexData::is_dir_context();
ctx.expect().times(1).returning(|_x| true);
let _result = PackageFileIndexData::new("good_directory").unwrap();
}
#[test]
fn test_bad_directory2() {
let ctx = MockPathInterface::is_dir_context();
ctx.expect().times(1).returning(|_x| false);
let result = PackageFileIndexData::new("bad_directory").err().unwrap();
assert_eq!(result, PackageFileIndexError::ArchiveRootNotADirectory);
}
#[test]
fn test_good_directory2() {
let ctx = MockPathInterface::is_dir_context();
ctx.expect().times(1).returning(|_x| true);
let _result = PackageFileIndexData::new("good_directory").unwrap();
}
}
所有这些测试都失败了,原因如下。我认为可用的模拟对象(测试发现了各种模拟上下文)未被正在运行的测试使用。
---- mock_is_dir::tests::test_good_directory1 stdout ----
thread 'mock_is_dir::tests::test_good_directory1' panicked at 'called `Result::unwrap()` on an `Err` value: ArchiveRootNotADirectory', src/mock_is_dir.rs:63:23
---- mock_is_dir::tests::test_bad_directory2 stdout ----
thread 'mock_is_dir::tests::test_bad_directory2' panicked at 'MockPathInterface::is_dir: Expectation(<anything>) called fewer than 1 times', src/mock_is_dir.rs:10:1
---- mock_is_dir::tests::test_good_directory2 stdout ----
thread 'mock_is_dir::tests::test_good_directory2' panicked at 'called `Result::unwrap()` on an `Err` value: ArchiveRootNotADirectory', src/mock_is_dir.rs:81:23
---- mock_is_dir::tests::test_bad_directory1 stdout ----
thread 'mock_is_dir::tests::test_bad_directory1' panicked at 'MockPackageFileIndexData::is_dir: Expectation(<anything>) called fewer than 1 times', src/mock_is_dir.rs:40:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
mock_is_dir::tests::test_bad_directory1
mock_is_dir::tests::test_bad_directory2
mock_is_dir::tests::test_good_directory1
mock_is_dir::tests::test_good_directory2
test result: FAILED. 0 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out
cfg-if
或类似的宏级编译时控制。我认为通用构造函数比使用宏更简洁,但可维护性让我担忧。如果在这里不能或不应该使用模拟,那么测试这种实现的惯用 Rust 方法是什么? - blueskyjunkie