Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an `std::env::Args` iterator for testing

Is there a way in Rust to create a std::env::Args from a Vec<String> in order to use it in a #[test] function?

I wish to test a function that gets a std::env::Args as an argument, but I don't know how to create such an object with a list of arguments I supply for the test.

I wasn't able to figure this one out from the docs, the source nor from Google searches.

like image 297
Chen Levy Avatar asked Nov 22 '17 18:11

Chen Levy


2 Answers

The fields of std::env::Args are not documented, and there doesn't appear to be a public function to create one with custom fields. So, you're outta luck there.

But since it's just "An iterator over the arguments of a process, yielding a String value for each argument" your functions can take a String iterator or Vec without any loss of functionality or type safety. Since it's just a list of Strings, it doesn't make much sense to arbitrarily limit your functions to strings which happen to come from the command line.

Looking through Rust's own tests, that's just what they do. There's a lot of let args: Vec<String> = env::args().collect();

There's even an example in rustbuild where they strip off the name of the program and just feed the list of arguments.

use std::env;

use bootstrap::{Config, Build};

fn main() {
    let args = env::args().skip(1).collect::<Vec<_>>();
    let config = Config::parse(&args);
    Build::new(config).build();
}

And bootstrap::Config::parse() looks like so:

impl Config {
    pub fn parse(args: &[String]) -> Config {
        let flags = Flags::parse(&args);
        ...

I'm not a Rust expert, but that seems to be how the Rust folks handle the problem.

like image 105
Schwern Avatar answered Oct 08 '22 00:10

Schwern


@Schwern's answer is good and it led me to this simpler version. Since std::env::Args implements Iterator with Item = String you can do this:

use std::env;

fn parse<T>(args: T)
where
    T: Iterator<Item = String>,
{
    for arg in args {
        // arg: String
        print!("{}", arg);
    }
}

fn main() {
    parse(env::args());
}

To test, you provide parse with an iterator over String:

#[test]
fn test_parse() {
   let args = ["arg1", "arg2"].iter().map(|s| s.to_string());
   parse(args);
}
like image 28
Rossman Avatar answered Oct 08 '22 00:10

Rossman