我的目标是测试一个转到标准输出的函数的输出。到目前为止,我最好的尝试是在测试中用字符串替换流。
这是我到目前为止所取得的成就:
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标准输出的函数的最佳解决方案是什么?有没有办法用字符串修复我的解决方案,还是应该寻找替代方案?
答案 0 :(得分:4)
write!
宏期望目标操作数实现 std::fmt::Write
或std::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()));
}