通过替代标准输出测试标准输出

Igo*_*vić 4 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());
}
Run Code Online (Sandbox Code Playgroud)

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

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

Fra*_*gné 5

write!宏观预期目标操作数来实现两种 std::fmt::Writestd::io::Write。自writeln!代表以来write!,这也适用于writeln!

的文档std::fmt::Write说:

io::Write性状优于实施这一特质

Stdout实施以来std::io::Write,您应该将代码范围从更改fmt::Writeio::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());
}
Run Code Online (Sandbox Code Playgroud)

为了提高性能,如果仅一个线程需要在stdout上写,请考虑将a StdoutLock而不是a 传递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()));
}
Run Code Online (Sandbox Code Playgroud)

  • 对于未来的读者,衬里 `fn hello(stdout: &amp;mut io::Write) {` 通常写为 `fn hello&lt;W: Write&gt;(stdout: W) {` 以实现单态化(这通常是您想要的)。 (3认同)