Rust测试工具库test-binary的使用,test-binary提供二进制测试框架和自动化测试功能

Rust测试工具库test-binary的使用

test-binary是一个用于管理和构建集成测试额外二进制文件的Rust工具库,它允许你将测试用的支持二进制文件作为常规Rust crate来组织。

使用场景

当你需要进行以下类型的测试时,test-binary会非常有用:

  • 子进程管理测试
  • 进程间通信测试
  • 平台工具测试

例如:

  • 测试代码对子进程退出状态的处理
  • 测试IPC通信时需要一个能发送预设回复的"mock"二进制程序

传统方式的限制

虽然不使用test-binary也能实现类似功能,但有以下限制:

  1. 使用src/bin[[bin]]的二进制文件:

    • 必须与整个crate共享依赖
    • 通过CARGO_BIN_EXE_<name>环境变量访问
  2. 示例二进制文件(examples/[[example]]):

    • 使用[dev-dependencies]
    • 没有对应的环境变量
    • 可能在测试运行时尚未构建完成
  3. 作为工作区crate组织:

test-binary解决方案

test-binary提供了一个简单接口来构建组织在crate单独目录下的额外二进制文件。

目录结构示例

├── Cargo.toml        (你的crate的manifest)
├── src
│  └── lib.rs         (你的crate的lib.rs)
├── tests
│  └── tests.rs       (你的crate的测试)
│
└── testbins          (所有额外二进制项目)
   ├── test-something (第一个额外二进制)
   │  ├── Cargo.toml  (额外二进制manifest)
   │  └── src
   │     └── main.rs  (额外二进制源文件)
   ├── test-whatever  (另一个额外二进制)
   │  ├── Cargo.toml
   │  └── src
   │     └── main.rs

基本用法

使用build_test_binary("test-something", "testbins"):

  • "test-something"是二进制名称(也是子目录名)
  • "testbins"是包含测试二进制项目的目录名

示例代码

// 构建测试二进制
let test_bin_path = build_test_binary("does-build", "testbins")
    .expect("error building test binary");

// 运行测试二进制
let mut test_bin_subproc = std::process::Command::new(test_bin_path)
    .spawn()
    .expect("error running test binary");

// 测试你的程序与mock二进制的交互行为
// 例如:发送stdin输入,断言stdout输出,检查IPC或副作用

assert!(test_bin_subproc
    .wait()
    .expect("error waiting for test binary")
    .success());

高级功能

  1. Builder API:如果需要设置不同profile或features,或需要更多控制目录结构

  2. build_test_binary_once!():懒加载构建二进制并缓存路径

完整示例

  1. 首先在项目根目录创建testbins目录
  2. testbins下创建测试二进制项目,例如test-example:
// testbins/test-example/src/main.rs
fn main() {
    println!("This is a test binary");
    std::process::exit(0);
}
  1. testbins/test-example/Cargo.toml中添加:
[package]
name = "test-example"
version = "0.1.0"
edition = "2021"

[dependencies]
  1. 在你的集成测试中使用:
// tests/integration_test.rs
use test_binary::build_test_binary;

#[test]
fn test_with_support_binary() {
    let bin_path = build_test_binary("test-example", "testbins")
        .expect("Failed to build test binary");
    
    let output = std::process::Command::new(bin_path)
        .output()
        .expect("Failed to run test binary");
    
    assert!(output.status.success());
    assert_eq!(String::from_utf8_lossy(&output.stdout), "This is a test binary\n");
}

注意事项

  • 可以在测试二进制的Cargo.toml中添加空[workspace]section
  • 最低支持的Rust版本:1.57
  • MIT许可证

1 回复

Rust测试工具库test-binary使用指南

test-binary是一个Rust测试工具库,专门用于测试二进制程序,提供了一套完整的二进制测试框架和自动化测试功能。它特别适合测试命令行应用程序和其他可执行程序。

主要特性

  • 执行二进制程序并捕获输出
  • 验证退出状态码
  • 检查标准输出和标准错误内容
  • 提供断言宏用于测试验证
  • 支持测试环境设置和清理

安装方法

在Cargo.toml中添加依赖:

[dev-dependencies]
test-binary = "0.4"

基本使用方法

1. 简单测试示例

use test_binary::build_test_binary;

#[test]
fn test_hello_world() {
    // 构建要测试的二进制程序
    let bin = build_test_binary!("../path/to/your/binary")
        .expect("failed to build test binary");
    
    // 执行程序并捕获输出
    let output = bin.output().expect("failed to execute binary");
    
    // 验证退出码是否为0(成功)
    assert!(output.status.success());
    
    // 验证标准输出是否包含特定文本
    assert!(String::from_utf8_lossy(&output.stdout).contains("Hello, world!"));
}

2. 带参数测试

use test_binary::build_test_binary;

#[test]
fn test_with_args() {
    let bin = build_test_binary!("../path/to/your/binary")
        .expect("failed to build test binary");
    
    // 执行带参数的程序
    let output = bin.args(&["--name", "Rust"]).output().unwrap();
    
    assert!(String::from_utf8_lossy(&output.stdout).contains("Hello, Rust!"));
}

3. 测试错误情况

use test_binary::build_test_binary;

#[test]
fn test_error_case() {
    let bin = build_test_binary!("../path/to/your/binary")
        .expect("failed to build test binary");
    
    let output = bin.args(&["--invalid-arg"]).output().unwrap();
    
    // 验证程序是否以错误码退出
    assert!(!output.status.success());
    
    // 验证错误输出
    assert!(String::from_utf8_lossy(&output.stderr).contains("invalid argument"));
}

高级功能

1. 测试环境设置

use test_binary::{build_test_binary, TestBinary};
use tempfile::tempdir;

#[test]
fn test_with_environment() {
    // 创建临时目录
    let temp_dir = tempdir().unwrap();
    
    let bin = build_test_binary!("../path/to/your/binary")
        .current_dir(temp_dir.path())  // 设置工作目录
        .env("MY_VAR", "test_value")   // 设置环境变量
        .expect("failed to build test binary");
    
    let output = bin.output().unwrap();
    // 进行断言...
}

2. 测试交互式程序

use test_binary::build_test_binary;
use std::io::Write;

#[test]
fn test_interactive() {
    let mut bin = build_test_binary!("../path/to/your/binary")
        .spawn()
        .expect("failed to spawn binary");
    
    // 向程序标准输入写入数据
    bin.stdin.as_mut().unwrap().write_all(b"input data\n").unwrap();
    
    // 等待程序结束
    let output = bin.wait_with_output().unwrap();
    
    // 进行断言...
}

最佳实践

  1. 将测试二进制程序放在项目的tests/目录中
  2. 为每个主要功能编写单独的测试用例
  3. 使用临时目录和文件进行需要文件系统操作的测试
  4. 清理测试创建的任何资源
  5. 测试正常情况和各种错误情况

test-binary库为Rust程序的二进制测试提供了强大而灵活的工具,特别适合测试命令行应用程序和其他需要执行环境验证的程序。

完整示例demo

下面是一个完整的测试示例,展示如何使用test-binary测试一个简单的命令行程序:

// 测试一个简单的命令行计算器程序

use test_binary::build_test_binary;
use tempfile::tempdir;

#[test]
fn test_calculator() {
    // 1. 测试加法功能
    let bin = build_test_binary!("../target/debug/calculator")
        .expect("failed to build test binary");
    
    let output = bin.args(&["add", "5", "3"]).output().unwrap();
    
    assert!(output.status.success());
    assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "8");
    
    // 2. 测试减法功能
    let output = bin.args(&["sub", "10", "4"]).output().unwrap();
    
    assert!(output.status.success());
    assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "6");
    
    // 3. 测试错误情况 - 缺少参数
    let output = bin.args(&["add", "5"]).output().unwrap();
    
    assert!(!output.status.success());
    assert!(String::from_utf8_lossy(&output.stderr).contains("not enough arguments"));
    
    // 4. 测试文件操作功能
    let temp_dir = tempdir().unwrap();
    let file_path = temp_dir.path().join("test.txt");
    
    let bin = build_test_binary!("../target/debug/calculator")
        .current_dir(temp_dir.path())
        .expect("failed to build test binary");
    
    let output = bin.args(&["create-file", "test.txt"]).output().unwrap();
    
    assert!(output.status.success());
    assert!(file_path.exists());
}

这个完整示例演示了:

  1. 测试不同的命令行参数组合
  2. 验证正常情况和错误情况的输出
  3. 使用临时目录进行文件系统操作测试
  4. 检查程序的退出状态码和输出内容
回到顶部