How to make a unit test in rust for a function that uses console input (io::stdin)?

hi, I'm new to Rust, I've been trying to find a solution to my problem on the internet, but everywhere (example) they recommend passing an option to a function, but I need the function to not receive anything

here is an example function

fn yes_or_no(prompt: &str) -> bool {
    let approvals_array = ["y", "yes", "т", "так", "д", "да"];

    print!("{} ", prompt);
    std::io::stdout().flush().unwrap();

    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    let input = input.trim().to_lowercase();

    return approvals_array.contains(&input.as_str());
}

please, help

The solution for testing functions that perform I/O is to avoid hardcoding the source to read from. Instead of making the function write to/read from stdout/stdin, make it take a Read and Write instead:

use std::io;

fn yes_or_no<R, W>(prompt: &str, mut reader: R, mut writer: W) -> io::Result<bool>
where
    R: io::BufRead,
    W: io::Write,
{
    let approvals_array = ["y", "yes", "т", "так", "д", "да"];

    write!(writer, "{} ", prompt);
    writer.flush()?;

    let mut input = String::new();
    reader.read_line(&mut input)?;
    let input = input.trim().to_lowercase();

    Ok(approvals_array.contains(&input.as_str()))
}

Then you can test it using e.g. io::Cursor and/or Vec: Playground

3 Likes

Is it possible to not pass mut reader: R, mut writer: W to the function, because then in all places where this function will be used, io::stdout() will have to be fussed, I think it will be redundant (unnecessary) because it will be everywhere standard input ?

No. But you can wrap it in another convenience function that always passes stdin/stdout, and use that in your code instead.

By the way, if this is a function always purely used for CLI, you probably shouldn't bother unit testing it. It will be obvious when it breaks.

1 Like

I'm trying to develop an application in a group to make sure that they understand correctly what behavior of the function I expect, I wanted to write unit tests that would reflect the nuances of behavior depending on certain input data, maybe there is a solution?

I've used this crate for testing an app processes stdin appropriately.
assert_cmd crate
You can build up unit tests in tests/cli.rs and run with cargo test.
This may be of use.

2 Likes