rust 如何模拟'std::fs::File'以便检查'File::set_len'是否在单元测试中正确使用?

wj8zmpe1  于 2022-12-04  发布在  其他
关注(0)|答案(1)|浏览(107)

当我检查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
}

关于生 rust :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`

P.S.在收到答案之前,我开始尝试tempfile板条箱:尽管https://docs.rs/tempfile/3.1.0/tempfile/#structs如此,理想的解决方案还是“内存中”,所以不能等待问题的答案:)。

iqxoj9l9

iqxoj9l91#

简而言之,如果一个函数需要std::fs::File的确切类型,就不能模拟std::fs::File--这不是Rust的工作方式。
但是,如果您可以控制foo,那么您可以轻松地发明一个具有set_len的特征,并使foo泛型化。(例如File),这将使foo()像以前一样接受File,但它也将接受实现该特征的任何其他内容,包括你在测试套件中创建的mock类型。2并且由于单态化,它的执行将和原始代码一样高效。3例如:

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(())
}

要模拟它,只需定义一个实现该特征的类型,并记录它是否被调用:

#[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));
}

Playground

相关问题