通过替换stdout来测试标准输出

时间:2018-01-23 00:06:31

标签: rust

我的目标是测试一个转到标准输出的函数的输出。到目前为止,我最好的尝试是在测试中用字符串替换流。

这是我到目前为止所取得的成就:

use std::io;
use std::fmt;

fn hello(stdout: &mut std::fmt::Write) {
    writeln!(stdout, "Hello world");
}

#[test]
fn hello_test() {
    let mut stdout = String::new();

    // pass fake stdout when calling when testing
    hello(&mut stdout);

    assert_eq!(stdout, "Hello world\n".to_string());
}

fn main() {
    // pass real stdout when calling from main

    hello(&mut io::stdout());
}

测试有效,但遗憾的是io::stdout()没有实现fmt::Write特征。

测试写入Rust标准输出的函数的最佳解决方案是什么?有没有办法用字符串修复我的解决方案,还是应该寻找替代方案?

1 个答案:

答案 0 :(得分:4)

write!宏期望目标操作数实现 std::fmt::Writestd::io::Write。由于writeln!委托给write!,这也适用于writeln!

std::fmt::Write的文档说明了这一点:

  

io::Write特征优于实施此特征

由于Stdout实现了std::io::Write,您应该将代码的范围从fmt::Write更改为io::Write。但请注意,String并未实现io::Write,因为io::Write接受任意字节,这些字节可能不是格式良好的UTF-8;您可以改用Vec<u8>

use std::io;

fn hello(stdout: &mut io::Write) {
    writeln!(stdout, "Hello world");
}

#[test]
fn hello_test() {
    let mut stdout = Vec::new();

    // pass fake stdout when calling when testing
    hello(&mut stdout);

    assert_eq!(stdout, b"Hello world\n");
}

fn main() {
    // pass real stdout when calling from main

    hello(&mut io::stdout());
}

为了提高性能,如果只需要在stdout上写入一个线程,请考虑将StdoutLock而不是Stdout传递给您的函数(使用Stdout,每次写入都会获取并释放一个锁)。

如果您真的更喜欢使用std::fmt::Write,那么您可以使用适配器结构将fmt::Write次调用转换为io::Write次来电。

use std::io;
use std::fmt;

struct WriteAdapter<W>(W);

impl<W> fmt::Write for WriteAdapter<W>
where
    W: io::Write,
{
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error)
    }

    fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), fmt::Error> {
        self.0.write_fmt(args).map_err(|_| fmt::Error)
    }
}

fn hello(stdout: &mut fmt::Write) {
    writeln!(stdout, "Hello world");
}

#[test]
fn hello_test() {
    let mut stdout = String::new();

    // pass fake stdout when calling when testing
    hello(&mut stdout);

    assert_eq!(stdout, "Hello world\n");
}

fn main() {
    // pass real stdout when calling from main

    hello(&mut WriteAdapter(io::stdout()));
}