» Rust创建命令行程序grep » 2. 开发 » 2.4 编写测试

编写测试

Rust 提供了强大的内置测试功能支持,使用非常方便。

Rust 使用 #[test] 属性来定义测试函数。这些函数通过该属性被标识出来,并在测试运行时被执行。

测试文件通常以被测试的模块命名,并添加 tests 后缀。例如,如果有一个名为 my_module 的模块,则相应的测试文件将是 my_module_tests.rs。 测试函数以 test_ 前缀命名,后跟一个描述性的名称,表示正在测试什么。函数名称建议使用蛇形命名(snake_case)。

在测试函数中,使用 assert! 宏及其相关宏(如 assert_eq!assert_ne!)进行断言。断言有助于验证实际结果是否与预期结果相符。

tests/lib_tests.rs:

use tempfile::tempdir;

use lr_grustep::{grep, grep_count, grep_recursive, GrepOptions, MatchItem, MatchResult};

#[test]
fn test_grep() {
    let pattern = "fn grep";
    let file_content = "This is a test line with fn grep in it.";
    let temp_dir = tempdir().expect("Failed to create temporary directory");
    let file_path = temp_dir.path().join("test_file.txt");

    std::fs::write(&file_path, file_content).expect("Failed to write to temporary file");

    let options = GrepOptions {
        ignore_case: false,
        invert_match: false,
    };

    let result = grep(pattern, &file_path, &options).unwrap();
    let matched_lines = result.get(file_path.to_string_lossy().as_ref()).unwrap();
    assert_eq!(matched_lines.len(), 1);

    // 检查匹配行的内容
    assert_eq!(matched_lines[0].line, file_content.trim());
}

#[test]
fn test_grep_count() {
    let mut result = MatchResult::new();
    let item = MatchItem {
        line_number: 1,
        line: String::from("Test line"),
    };
    result.insert(String::from("test_file.txt"), vec![item]);

    let count = grep_count(&result);
    assert_eq!(count, 1);
}

#[test]
fn test_grep_recursive() {
    let pattern = "TODO";
    let temp_dir = tempdir().expect("Failed to create temporary directory");
    let nested_dir = temp_dir.path().join("nested");
    std::fs::create_dir(&nested_dir).expect("Failed to create nested directory");

    let file_content = "TODO: Implement this feature.";
    let file_path = temp_dir.path().join("test_file.txt");
    let nested_file_path = nested_dir.join("nested_file.txt");

    std::fs::write(&file_path, file_content).expect("Failed to write to temporary file");
    std::fs::write(&nested_file_path, file_content).expect("Failed to write to nested file");

    let options = GrepOptions {
        ignore_case: false,
        invert_match: false,
    };

    let result = grep_recursive(pattern, temp_dir.path(), &options).unwrap();
    let matched_lines = result.get(file_path.to_string_lossy().as_ref()).unwrap();
    assert_eq!(matched_lines.len(), 1);

    // 检查匹配行的内容
    assert_eq!(matched_lines[0].line, file_content.trim());

    // 检查嵌套文件的内容
    let nested_matched_lines = result
        .get(nested_file_path.to_string_lossy().as_ref())
        .unwrap();
    assert_eq!(nested_matched_lines.len(), 1);
    assert_eq!(nested_matched_lines[0].line, file_content.trim());
}

使用 cargo test 命令运行测试:

cargo test

应该会得到如下测试结果:

     Running tests/lib_tests.rs (target/debug/deps/lib_tests-a90b7c416c14752c)

running 3 tests
test test_grep_count ... ok
test test_grep ... ok
test test_grep_recursive ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

如果有错误,你将会看到详细的错误消息,例如:

     Running tests/lib_tests.rs (target/debug/deps/lib_tests-a90b7c416c14752c)

running 3 tests
test test_grep_count ... ok
test test_grep ... FAILED
test test_grep_recursive ... ok

failures:

---- test_grep stdout ----
thread 'test_grep' panicked at tests/lib_tests.rs:21:5:
assertion `left == right` failed
  left: 0
 right: 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    test_grep

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test lib_tests`

然后,你可以根据此错误消息修复代码逻辑。