如何在assert_cmd中使用stdin实现CLI应用集成测试?
解决CLI交互式测试的两个问题
问题1:等待/检测提示信息
要检测程序输出的提示,需要手动监听子进程的stdout,直到出现目标提示字符串,确认程序进入等待输入的状态后再发送响应。
问题2:自动发送yes响应
结合异步IO控制子进程的输入输出,先启动命令,读取stdout捕获提示,再写入yes\n到stdin,最后验证执行结果。
具体实现步骤
- 确保依赖包含
tokio(用于异步IO操作),修改Cargo.toml:
[dependencies] assert_cmd = "2.0" predicates = "3.0" tokio = { version = "1.0", features = ["full"] }
- 修改测试代码:
use assert_cmd::Command; use predicates::prelude::*; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; #[tokio::test] async fn add_test() -> Result<(), Box<dyn std::error::Error>> { // 启动CLI命令 let mut cmd = Command::cargo_bin("hund")? .args(&["-f", "file.txt"]) .spawn()?; // 获取子进程的stdout和stdin句柄 let mut stdout = cmd.stdout.take().unwrap(); let mut stdin = cmd.stdin.take().unwrap(); // 循环读取stdout,直到捕获到你的提示字符串(替换成实际提示内容) let mut buf = String::new(); while !buf.contains("请输入yes继续: ") { stdout.read_line(&mut buf).await?; } // 向stdin发送yes响应,记得加换行符 stdin.write_all(b"yes\n").await?; stdin.flush().await?; // 等待命令执行完成,验证结果 let output = cmd.wait_with_output().await?; assert!(output.status.success()); assert!(predicate::str::contains("File modified").eval(&String::from_utf8_lossy(&output.stdout))); Ok(()) }
说明
- 用
tokio::test标记异步测试,因为需要异步处理子进程的IO交互 spawn()方法启动命令,而非直接调用assert(),这样能手动控制输入输出- 替换代码中的提示字符串(
请输入yes继续:)为你CLI程序实际输出的提示内容 - 如果你的CLI用
std::io::stdin().read_line()读取输入,yes\n的格式是正确的,因为read_line会读取到换行符为止
内容的提问来源于stack exchange,提问作者Maria
相关产品推荐
相关产品推荐

