gpt4 book ai didi

unit-testing - Rust 模拟另一个结构实现消耗的特征

转载 作者:行者123 更新时间:2023-12-03 11:30:04 25 4
gpt4 key购买 nike

我严格地尝试模拟 std::path::PathBuf.is_dir 方法,但我认为这里有一个更通用的用例,实际上这是关于模拟外部功能。

我创建了一个特征,它封装了 PathBuf.is_dir 方法,理论上,根据 mockall documentation应该使我能够模拟我的 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

最佳答案

不幸的是,您正在做的事情不会奏效,至少不会以您想要的方式奏效。 #[automock]正在创建 struct称为 MockPathInterface .然后,您可以将该类型传递给期望实现 PathInterface 的函数。 .它不能改变实现该特征的现有结构的行为,至少不能直接改变。您确实设置了 MockPathInterface在你的测试中正确,但没有任何关联它与 PackageFileInfoData , 所以它永远不会被使用。

你可以这样做的一种方法是我修改你的代码来传递行为,然后你可以传递你的模拟行为。例如:

#[automock]
pub 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<PI: PathInterface>(archive_root: &str) -> Result<PackageFileIndexData, PackageFileIndexError> {
let archive_root_path = PathBuf::from(archive_root.clone());

if !PI::is_dir(&archive_root_path) {
return Err(PackageFileIndexError::ArchiveRootNotADirectory);
}

Ok(PackageFileIndexData { archive_root_path })
}
}

注意我们在 new 中使用了一个通用参数传递实现特征的类型的静态方法。另请注意,我们更改了 is_dir调用以引用该通用类型。

然后您可以修改您的测试以使用该类型参数(下面是一个示例):

#[test]
fn test_bad_directory() {
let ctx = MockPathInterface::is_dir_context();
ctx.expect().times(1).returning(|_x| false);

let result = PackageFileIndexData::new::<MockPathInterface>("bad_directory").err().unwrap();

assert_eq!(result, PackageFileIndexError::ArchiveRootNotADirectory);
}

此处唯一的变化是使用涡轮鱼 (::<>) 运算符将类型传递到 new 中静态方法。

这些更改在美学上很丑陋,我不一定建议您在任何地方都使用这种模式 - 如果您真的必须模拟很多这样的行为,那基本上是无法维护的。但这说明了如何做到这一点,以及 Rust 中模拟的局限性。

关于unit-testing - Rust 模拟另一个结构实现消耗的特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60997762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com