Rust目录测试工具库dir-test的使用,提供高效文件系统目录操作与测试功能

Rust目录测试工具库dir-test的使用,提供高效文件系统目录操作与测试功能

CI Crates.io License

dir-test 提供了一个宏,可以从目录中的文件生成单个测试用例。

使用

在您的 Cargo.toml 中添加以下依赖:

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

基本用法

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/fixtures",
    glob: "**/*.txt",
)]
fn mytest(fixture: Fixture<&str>) {
    // 文件内容和文件的绝对路径如下获取
    let content = fixture.content();
    let path = fixture.path();

    // 在此处编写测试代码
    // ...
}

假设您的项目结构如下,则上述代码会生成两个测试用例 mytest__foo()mytest__fixtures_a_bar()

my-crate/
├─ fixtures/
│  ├─ foo.txt
│  ├─ fixtures_a/
│  │  ├─ bar.txt
├─ src/
│  ├─ ...
│  ├─ lib.rs
├─ Cargo.toml
├─ README.md

🔽

#[test]
fn mytest__foo() {
    mytest(fixture);
}

#[test]
fn mytest__fixtures_a_bar() {
    mytest(fixture);
}

注意: dir 参数必须指定为绝对路径,因为当前过程宏系统的限制。可以考虑使用环境变量,dir-test 库会在内部解析环境变量。

自定义加载器

您可以指定一个自定义加载器函数来从文件路径加载文件内容。加载器将传递 &'static str 文件路径,并可以返回任意类型。

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/fixtures",
    glob: "**/*.txt",
    loader: std::fs::read_to_string,
)]
fn test(fixture: Fixture<std::io::Result<String>>) {
    let content = fixture.content().unwrap();

    // ...
}

如果未指定加载器函数,则默认内容类型为 &'static str

自定义测试名称

可以通过指定 postfix 参数来自定义测试名称。postfix 会附加到测试名称后面。

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/fixtures",
    glob: "**/*.txt",
    postfix: "custom", // `_custom` 会附加到测试名称
)]
fn test(fixture: Fixture<std::io::Result<String>>) {
    // ...
}

测试属性

测试属性可以通过 dir_test_attr 属性指定。dir_test_attr 中的属性将应用于所有生成的测试。

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/fixtures",
    glob: "**/*.txt",
)]
#[dir_test_attr(
    #[wasm_bindgen_test]
    #[cfg(target_family = "wasm")]
)]
fn wasm_test(fixture: Fixture<std::io::Result/String>>) {
    // ...
}

注意: dir_test_attr 属性必须在 dir_test 之后指定。

返回值类型

测试可以有返回值类型,允许在测试中使用 Result<T, E> 类型。

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/fixtures",
    glob: "**/*.txt",
)]
fn test(fixture: Fixture<&str>) -> std::io::Result<()> {
    // ...
}

完整示例

下面是一个完整的示例,展示如何使用 dir-test 进行目录测试:

  1. 首先在项目中创建测试目录和文件:
my_project/
├─ tests/
│  ├─ fixtures/
│  │  ├─ test1.txt
│  │  ├─ subdir/
│  │  │  ├─ test2.txt
├─ Cargo.toml
  1. Cargo.toml 中添加依赖:
[dev-dependencies]
dir-test = "0.4"
  1. 创建测试文件 tests/directory_tests.rs:
use dir_test::{dir_test, Fixture};

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/tests/fixtures",
    glob: "**/*.txt",
    loader: std::fs::read_to_string,
)]
fn test_file_content(fixture: Fixture<std::io::Result<String>>) -> std::io::Result<()> {
    let content = fixture.content()?;
    let path = fixture.path();
    
    println!("Testing file: {}", path.display());
    println!("Content length: {}", content.len());
    
    // 这里可以添加具体的测试逻辑
    assert!(!content.is_empty());
    
    Ok(())
}

#[dir_test(
    dir: "$CARGO_MANIFEST_DIR/tests/fixtures",
    glob: "**/*.txt",
    postfix: "check_extension",
)]
fn test_file_extension(fixture: Fixture<&str>) {
    let path = fixture.path();
    assert_eq!(path.extension().unwrap(), "txt");
}
  1. 运行测试:
cargo test

这个示例会:

  1. tests/fixtures 目录及其子目录中查找所有 .txt 文件
  2. 生成两个测试集:
    • test_file_content 测试每个文件的内容不为空
    • test_file_extension_check_extension 测试每个文件的扩展名是 .txt

每个找到的 .txt 文件都会生成对应的测试用例,测试名称会自动包含文件路径信息。


1 回复

Rust目录测试工具库dir-test的使用指南

dir-test是一个专门用于文件系统目录操作与测试的Rust库,它提供了高效、便捷的方式来创建、管理和验证目录结构,特别适合在单元测试和集成测试场景中使用。

主要功能

  1. 临时目录的创建与自动清理
  2. 目录结构的快速搭建
  3. 文件内容的生成与验证
  4. 目录比较与断言

安装方法

Cargo.toml中添加依赖:

[dependencies]
dir-test = "0.3"

基本使用方法

1. 创建临时目录

use dir_test::{dir_test, Fixture};

#[dir_test]
fn test_example() {
    // 在当前测试函数中创建一个临时目录
    let fixture = Fixture::blank();
    
    // 获取临时目录路径
    let temp_dir = fixture.path();
    println!("临时目录: {:?}", temp_dir);
    
    // 测试结束后自动删除临时目录
}

2. 使用预设目录结构

use dir_test::{dir_test, Fixture};

#[dir_test(
    dir = "fixtures/my_test_case",  // 指定预设目录
)]
fn test_with_fixture() {
    let fixture = Fixture::current();
    
    // 读取预设目录中的文件
    let content = std::fs::read_to_string(fixture.path().join("example.txt")).unwrap();
    assert_eq!(content, "Hello, dir-test!");
}

3. 动态创建目录结构

use dir_test::{dir_test, Fixture};

#[dir_test]
fn test_dynamic_creation() {
    let fixture = Fixture::blank();
    
    // 创建子目录
    fixture.create_dir("subdir");
    
    // 创建文件并写入内容
    fixture.create_file("subdir/file.txt", "Test content");
    
    // 验证文件存在
    assert!(fixture.path().join("subdir/file.txt").exists());
}

高级用法

1. 使用模板目录

use dir_test::{dir_test, Fixture};

#[dir_test(
    fixture = "fixtures/template",
    config = r#"
        [files]
        "config.toml" = { content = "port = 8080" }
        "README.md" = { content = "# Project\n\nTest project" }
    "#
)]
fn test_with_template() {
    let fixture = Fixture::current();
    
    // 模板文件和动态生成的文件都可用
    assert!(fixture.path().join("existing_file.txt").exists());
    assert!(fixture.path().join("config.toml").exists());
}

2. 目录比较测试

use dir_test::{dir_test, Fixture, assert_dir_eq};
use std::path::Path;

#[dir_test]
fn test_directory_comparison() {
    let fixture = Fixture::blank();
    
    // 创建预期目录结构
    fixture.create_dir("expected");
    fixture.create_file("expected/file1.txt", "content1");
    fixture.create_file("expected/file2.txt", "content2");
    
    // 创建实际目录结构
    fixture.create_dir("actual");
    fixture.create_file("actual/file1.txt", "content1");
    fixture.create_file("actual/file2.txt", "content2");
    
    // 比较两个目录
    assert_dir_eq!(
        fixture.path().join("expected"),
        fixture.path().join("actual")
    );
}

配置选项

dir-test支持通过属性宏配置测试环境:

#[dir_test(
    dir = "fixtures/base",  // 基础目录
    fixture = "fixtures/template",  // 模板目录
    config = r#"
        [files]
        "dynamic.txt" = { content = "Generated content" }
    "#,
    clean = false  // 测试结束后不自动清理
)]
fn test_with_config() {
    // 测试代码
}

实际应用示例

测试文件处理函数

use dir_test::{dir_test, Fixture};

fn process_directory(dir: &std::path::Path) -> usize {
    // 假设这是一个处理目录的函数,返回处理的文件数
    std::fs::read_dir(dir).unwrap().count()
}

#[dir_test(
    fixture = "fixtures/sample_data",
    config = r#"
        [files]
        "new_file.txt" = { content = "additional file" }
    "#
)]
fn test_process_directory() {
    let fixture = Fixture::current();
    let file_count = process_directory(fixture.path());
    
    // 验证处理了正确数量的文件
    assert_eq(file_count, 3); // 假设fixture/sample_data有2个文件,加上动态创建的1个
}

完整示例代码

下面是一个结合了多种功能的完整示例:

use dir_test::{dir_test, Fixture, assert_dir_eq};
use std::fs;

// 测试文件处理函数 - 统计目录中特定扩展名的文件数量
fn count_files_by_extension(dir: &std::path::Path, ext: &str) -> usize {
    dir.read_dir()
        .unwrap()
        .filter_map(|entry| {
            let entry = entry.unwrap();
            if entry.path().extension().map(|e| e == ext).unwrap_or(false) {
                Some(entry.path())
            } else {
                None
            }
        })
        .count()
}

#[dir_test(
    fixture = "fixtures/template",  // 使用模板目录
    config = r#"
        [files]
        "config.toml" = { content = "port = 8080" }
        "data.json" = { content = "{}" }
        "log.txt" = { content = "some logs" }
    "#,
    clean = true  // 测试结束后自动清理
)]
fn test_file_operations() {
    let fixture = Fixture::current();
    let temp_dir = fixture.path();
    
    // 验证模板文件存在
    assert!(temp_dir.join("existing_file.txt").exists());
    
    // 验证动态创建的文件存在
    assert!(temp_dir.join("config.toml").exists());
    assert!(temp_dir.join("data.json").exists());
    assert!(temp_dir.join("log.txt").exists());
    
    // 测试文件处理函数
    let json_count = count_files_by_extension(temp_dir, "json");
    assert_eq!(json_count, 1);
    
    let txt_count = count_files_by_extension(temp_dir, "txt");
    assert_eq!(txt_count, 2);  // existing_file.txt + log.txt
    
    // 创建预期目录结构
    let expected_dir = temp_dir.join("expected");
    fs::create_dir(&expected_dir).unwrap();
    fs::write(expected_dir.join("test1.txt"), "test").unwrap();
    fs::write(expected_dir.join("test2.txt"), "test").unwrap();
    
    // 创建实际目录结构
    let actual_dir = temp_dir.join("actual");
    fs::create_dir(&actual_dir).unwrap();
    fs::write(actual_dir.join("test1.txt"), "test").unwrap();
    fs::write(actual_dir.join("test2.txt"), "test").unwrap();
    
    // 比较两个目录
    assert_dir_eq!(expected_dir, actual_dir);
}

这个完整示例展示了:

  1. 使用模板目录和动态配置创建测试环境
  2. 测试自定义的文件处理函数
  3. 动态创建目录结构进行比较测试
  4. 自动清理测试环境

dir-test库通过提供简洁的API和自动化的目录管理,大大简化了涉及文件系统操作的测试编写工作,是Rust生态中测试文件相关功能的理想选择。

回到顶部