如何测试标准输入和标准输出?

33
我希望编写一个提示函数,它将传入的字符串发送到标准输出(stdout),然后返回从标准输入(stdin)读取的字符串。我该如何测试它?
以下是这个函数的示例:
fn prompt(question: String) -> String {
    let mut stdin = BufferedReader::new(stdin());
    print!("{}", question);
    match stdin.read_line() {
        Ok(line) => line,
        Err(e)   => panic!(e),
    }
}

以下是我的测试尝试

#[test]
fn try_to_test_stdout() {
    let writer: Vec<u8> = vec![];
    set_stdout(Box::new(writer));
    print!("testing");
// `writer` is now gone, can't check to see if "testing" was sent
}
2个回答

50

使用依赖注入。将其与泛型和单态性耦合,您不会失去任何性能:

use std::io::{self, BufRead, Write};

fn prompt<R, W>(mut reader: R, mut writer: W, question: &str) -> String
where
    R: BufRead,
    W: Write,
{
    write!(&mut writer, "{}", question).expect("Unable to write");
    let mut s = String::new();
    reader.read_line(&mut s).expect("Unable to read");
    s
}

#[test]
fn test_with_in_memory() {
    let input = b"I'm George";
    let mut output = Vec::new();

    let answer = prompt(&input[..], &mut output, "Who goes there?");

    let output = String::from_utf8(output).expect("Not UTF-8");

    assert_eq!("Who goes there?", output);
    assert_eq!("I'm George", answer);
}

fn main() {
    let stdio = io::stdin();
    let input = stdio.lock();

    let output = io::stdout();

    let answer = prompt(input, output, "Who goes there?");
    println!("was: {}", answer);
}
在许多情况下,您可能希望将错误传播回调用者而不是使用expect,因为IO是故障常发生的地方。
这可以扩展到方法而不仅限于函数
use std::io::{self, BufRead, Write};

struct Quizzer<R, W> {
    reader: R,
    writer: W,
}

impl<R, W> Quizzer<R, W>
where
    R: BufRead,
    W: Write,
{
    fn prompt(&mut self, question: &str) -> String {
        write!(&mut self.writer, "{}", question).expect("Unable to write");
        let mut s = String::new();
        self.reader.read_line(&mut s).expect("Unable to read");
        s
    }
}

#[test]
fn test_with_in_memory() {
    let input = b"I'm George";
    let mut output = Vec::new();

    let answer = {
        let mut quizzer = Quizzer {
            reader: &input[..],
            writer: &mut output,
        };

        quizzer.prompt("Who goes there?")
    };

    let output = String::from_utf8(output).expect("Not UTF-8");

    assert_eq!("Who goes there?", output);
    assert_eq!("I'm George", answer);
}

fn main() {
    let stdio = io::stdin();
    let input = stdio.lock();

    let output = io::stdout();

    let mut quizzer = Quizzer {
        reader: input,
        writer: output,
    };

    let answer = quizzer.prompt("Who goes there?");
    println!("was: {}", answer);
}

您能否评论一下为什么这不会影响性能?是因为println!只是调用了writeln!(io::stdout(),...吗? - Frank Meulenaar
4
@FrankMeulenaar println! 最终使用 stdout,但不完全是那种方式。反过来问——为什么它会失去性能呢? - Shepmaster

0
补充@shepmaster的回答。如果想要测试多行输入,可以使用换行符分隔每一行。例如:
let input = b"I'm George\nI'm Cathy\nI'm Hussein";
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接