Grz*_*cki 5 unit-testing mocking rust
据我检查,File::set_len(..)它似乎是为 实现的struct File,但不是通过 Trait 实现的。
目标:测试foo以读/写方式打开文件,执行以下操作:读取、写入、查找和将文件修剪到一定大小。我们喜欢在测试中提供文件的初始状态,并检查结果。最好是在内存中。
如何测试依赖的代码set_len?(io::Seek或者其他特征到目前为止没有帮助)。
我想嘲笑它。
让我们做一个玩具示例,以使讨论更容易:
#![allow(unused_variables)]
use std::error::Error;
use std::fs::File;
use std::io::Cursor;
// assumes that file is open in Read/Write mode
// foo performs reads and writes and Seeks
// at the end wants to trim size of file to certain size.
fn foo(f: &mut File) -> Result<(), Box<dyn Error>> {
f.set_len(0)?;
Ok(())
}
fn main () -> Result<(), Box<dyn Error>> {
let mut buf = Vec::new();
let mut mockfile = Cursor::new(&buf);
// we would like to supply foo
// with "test" representation of initial file state
foo(&mut mockfile)
// and check afterwards if resulting contents (=> size)
// of file match expectations
}
Run Code Online (Sandbox Code Playgroud)
关于 rust-play :https://play.rust-lang.org/? version=stable&mode=debug&edition=2018&gist=950a94504168d51f043966288fae3bca
错误:
error[E0308]: mismatched types
--> src/main.rs:15:9
|
15 | foo(&mut mockfile)
| ^^^^^^^^^^^^^ expected struct `File`, found struct `std::io::Cursor`
Run Code Online (Sandbox Code Playgroud)
PS在收到答案之前,我开始尝试创建箱子tempfile: https: //docs.rs/tempfile/3.1.0/tempfile/#structs。尽管如此,理想的解决方案是“内存中”,所以迫不及待地等待问题的答案:)。
简而言之,你不能模拟std::fs::File一个需要精确类型的函数——这不是 Rust 的工作方式。
然而,如果你可以控制foo,你可以很容易地发明一个具有该特征的特征set_len并使foo该特征变得通用。由于这是您的特征,因此您可以为其他地方定义的类型(例如File)实现它,这将像以前一样foo()接受。File但它也接受任何其他实现该特征的东西,包括您在测试套件中创建的模拟类型。由于单态化,它的执行将与原始代码一样高效。例如:
pub trait SetLen {
fn set_len(&mut self, len: u64) -> io::Result<()>;
}
impl SetLen for File {
fn set_len(&mut self, len: u64) -> io::Result<()> {
File::set_len(self, len)
}
}
pub fn foo(f: &mut impl SetLen) -> Result<(), Box<dyn Error>> {
f.set_len(0)?;
Ok(())
}
// You can always pass a `File` to `foo()`:
fn main() -> Result<(), Box<dyn Error>> {
let mut f = File::create("bla")?;
foo(&mut f)?;
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
要模拟它,您只需定义一个实现该特征的类型并记录它是否被调用:
#[derive(Debug, Default)]
struct MockFile {
set_len_called: Option<u64>,
}
impl SetLen for MockFile {
fn set_len(&mut self, len: u64) -> io::Result<()> {
self.set_len_called = Some(len);
Ok(())
}
}
#[test]
fn test_set_len_called() {
let mut mf = MockFile::default();
foo(&mut mf).unwrap();
assert_eq!(mf.set_len_called, Some(0));
}
Run Code Online (Sandbox Code Playgroud)