如何在 Rust 中使用 clap 测试 CLI 参数?

rdx*_*dkr 8 testing command-line-interface rust clap

使用 为 CLI 程序编写测试的最惯用的方法是什么clap?我目前正在这样做:

#[derive(Debug, Parser)]
#[clap(author, version, about)]
pub struct StructArgs {
    #[clap(subcommand)]
    pub command_type: CommandType,
}

#[derive(Debug, Subcommand)]
pub enum CommandType {
    Command1(Command1Args),
    ...
}

#[derive(Debug, Args)]
pub struct Command1Args {
    pub field: String,
    ...
}

impl Command1Args {
    ...
}

#[test]
fn test_do_stuff() {
    let args = StructArgs::try_parse_from(
        std::iter::once("<PROGRAM NAME>")
        .chain(
            ["<ARG 1>", ..., "<ARG n>"]
            .iter()
            .cloned()
        )
    );

    if let CommandType::Command1(command1_args) = args.command_type {
        // do stuff with command1_args
    } else {
        panic!();
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,我传递给clap参数迭代器,然后检查解析的命令结构是否与CommandType我期望的匹配,然后继续测试其方法和内部状态。else 分支中的 in panicthe else 分支用于如果由于某种原因我得到一个意外的测试失败CommandType,这意味着很可能我在迭代器中写了一些错误的东西。

这可以进一步改进吗?

Mar*_*kus 1

Chayim 说你不应该测试 Clap 的说法当然有道理。但在某些情况下,情况并非如此,为使用 clap 实现的 CLI 解析器编写单元测试确实有意义。

就我而言,我想定义一组仅在设置了某个标志时才需要的参数,如果设置了,则需要该组的所有参数。在 Clap 中定义这并不是一件小事,测试它是有意义的,例如像这样(使用 Clap 4.4):

#[cfg(test)]
mod tests {

  use super::*;

  #[test]
  fn oidc_all_or_nothing() {
    /* Call it without any OIDC args, just graqphiql; must work */
    if let Err(err) = AppConfig::try_parse_from(vec!["aisrv", "--enable-graphiql"].iter()) {
      panic!("Just --enable-graphiql failed: {err}");
    }

    /* Call with only one OIDC arg, must fail */
    if AppConfig::try_parse_from(vec!["aisrv", "--oidc-idp-url", "tescht"].iter()).is_ok() {
      panic!("Just one OIDC arg did not fail!");
    }

    /* Call with all OIDC args, must succeed */
    if let Err(err) = AppConfig::try_parse_from(
      vec![
        "aisrv",
        "--oidc-idp-url",
        "tescht",
        "--oidc-super-admin-user",
        "tescht",
        "--oidc-super-admin-password",
        "tescht",
        "--oidc-super-admin-client-id",
        "tescht",
        "--oidc-super-admin-client-secret",
        "tescht",
        "--oidc-aud",
        "tescht",
      ]
      .iter(),
    ) {
      panic!("All OIDC args passed, but still fails: {err}");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)