0

So I built a small Rust CLI tool with structopt and now want to add some tests but didn't find any useful docs/ examples about how to write it

use structopt::StructOpt;


#[derive(Debug, StructOpt)]
#[structopt(name = "example")]
struct Cli {
    domain: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Cli::from_args();

    let url = format!("https://example.com/{}", args.domain);
    let resp = ureq::get(&url.to_string()).call().into_json()?;

    println!("Has data for domain: {}", args.domain);
    Ok(())
}

I'm about to have tests something like this

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn valid_domain() {
        // Example functions
        let cli = Cli::build_command(domain: "google.com");
        let output = cli.run_command();

        assert!(output.contains("Has data"));
    }
}

I now can resolve my issue with assert_cmd crate, some of my tests

mod tests {
    use assert_cmd::Command;

    #[test]
    fn valid_domain() {
        let mut cmd = Command::cargo_bin("example").unwrap();
        let output = cmd.arg("google.com").unwrap();
        let output_str = String::from_utf8(output.stdout).unwrap();
        assert!(output_str.contains("Has data"));
    }

    #[test]
    #[should_panic(expected = "Invalid domain")]
    fn invalid_domain() {
        Command::cargo_bin("example").unwrap().arg("google").unwrap();
    }
}
Thong Nguyen
  • 143
  • 3
  • 10
  • 2
    You shouldn't use `structopt` for new projects as [it's features are integrated into `clap` now](https://crates.io/crates/structopt#user-content-maintenance). – cafce25 Jul 13 '23 at 14:47
  • Got it, my tool was written with `structopt = "0.3.25"` and want to write some tests – Thong Nguyen Jul 13 '23 at 14:54

1 Answers1

2

You can invoke the parser with Cli::from_iter(["yourbinary", "your", "arguments"]) but after that you're on your own since structopt only provides commandline parsing.

It's probably best to split main into testable functions/methods, if you go by your wish API something like

impl Cli {
    fn run_command(&self) -> String {
        todo!() // your business logic
    }
}

and use that in the tests and in main instead of the code you currently have.

cafce25
  • 15,907
  • 4
  • 25
  • 31