Rust测试断言库hamcrest2的使用:增强单元测试和集成测试的断言功能与匹配器

Rust测试断言库hamcrest2的使用:增强单元测试和集成测试的断言功能与匹配器

Hamcrest2是Hamcrest库的Rust移植版本,它是一个维护良好的fork,提供了额外的匹配器、更好的文档以及对Rust 2018 edition的支持。

安装

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

[dev-dependencies]
hamcrest2 = "*"

使用

最简单的方式是导入所有匹配器:

use hamcrest2::prelude::*;

如果需要选择性导入,请确保同时导入HamcrestMatcher trait。

示例代码

通用匹配器

// 相等和不等匹配
assert_that!(1, eq(1));  // 也可以使用equal_to()
assert_that!(1, not(eq(2)));

// 比较匹配
assert_that!(1, lt(2));   // 也可以使用less_than()
assert_that!(1, leq(1));  // 也可以使用less_than_or_equal_to()
assert_that!(2, gt(1));   // 也可以使用greater_than()
assert_that!(2, geq(2));  // 也可以使用greater_than_or_equal_to()

// 类型匹配
assert_that!(123usize, type_of::<usize>());
assert_that!("test", type_of::<&str>());

// 正则匹配
assert_that!("1234", matches_regex(r"\d"));
assert_that!("abc", does_not(match_regex(r"\d")));

数值匹配器

assert_that!(1e-40f32, close_to(0.0, 0.01));
assert_极客时间
assert_that!(1e-40f32, not(close_to(0.0, 0.000001)));

文件系统匹配器

let path = Path::new("./README.md");
assert_that!(path, path_exists());
assert_that!(path, file_exists());
assert_that!(path, not(dir_exists()));

Option和Result匹配

// has匹配器
let var: Option<i8> = Some(5);
assert_that!(var, has(5));

let var: Result<i8, String> = Ok(5);
assert_that!(var, has(5));

// ok匹配器
let var: Result<i8, String> = Ok(5);
assert_that!(var, ok());
assert_that!(&var, ok());

assert_that!(Ok极客时间(5), ok::<i8, String>());
assert_that!(&Ok(5), ok::<i8, String>());

let var: Result<i8, String> = Err("bad!".to_string());
assert_that!(var, not(ok()));
assert_that!(&var, not(ok()));

// err匹配器
let var: Result<i8, String> = Err("bad!".to_string());
assert_that!(var, err());
assert_that!(&var, err());

assert_that!(Err("bad!".to_string()), err::<i8, String>());
assert_that!(&Err("bad!".to_string()), err::<i8, String>());

let var: Result<i8, String> = Ok(5);
assert_that!(var, not(err()));
assert_that!(&var, not(err()));

// some匹配器
let var: Option<i8> = Some(5);
assert_that!(var, some());
assert_that!(&var, some());

assert_that!(Some(1), some::<u8>());
assert_that!(&Some(1), some::<u8>());

let var: Option<i8> = None;
assert_that!(var, not(some()));
assert_that!(&var, not(some()));

// none匹配器
let var: Option<i8> = None;
assert_that!(var, none());
assert_that!(&var, none());

assert_that!(None, none::<u8>());
assert_that!(&None, none::<u8>());
assert_that!(Some(1), not(none::<u8>()));
assert_that!(&Some(1), not(none::<u8>()));

集合匹配器

assert_that!(&vec!(1, 2, 3), contains(vec!(1, 2)));
assert_that!(&vec!(1, 2, 3), contains(1));
assert_that!(&vec!(1, 2, 3), not(contains(4i)));

assert_that!(&vec!(1, 2, 3), contains(vec!(1, 2, 3)).exactly());
assert_that!(&vec!(1, 2, 3), not(contains(vec!(1, 2)).exactly()));

assert_that!(&vec!(1, 2, 3), contains(vec!(1, 2)).in_order());
assert_that!(&vec!(1, 2, 3), not(contains(vec!(1, 3)).in_order()));

// 长度匹配
assert_that!(&vec!(1, 2, 3), len(3));
assert_that!(&vec!(1, 2, 极客时间3), not(len(4)));

// 空集合匹配
assert_that!(&Vec::<i32>::new(), empty());
assert_that!(&vec![1, 2, 3], not(empty()));

复合匹配器

// all匹配器
assert_that!(4, all!(lt(5), gt(3)));  // 也可以使用and!()
assert_that!(
    &vec![1, 2, 3],
    all!(contains(vec![1, 2]), not(contains(vec![4])))
);

// any匹配器
assert_that!(4, any!(less_than(2), greater_than(3)));  // 也可以使用or!()
assert_that!(
    &vec![1, 2, 3],
    any!(contains(vec![1, 2, 5]), not(contains(vec![4])))
);

其他匹配器

// 布尔匹配
assert_that!(true, is(true));
assert_that!(false, is(false));

// 任意值匹配
assert_that!(42, anything());
assert_that!("test", is(anything()));

完整示例Demo

// 示例测试模块
#[cfg(test)]
mod tests {
    use hamcrest2::prelude::*;
    use std::path::Path;
    
    #[test]
    fn test_basic_assertions() {
        // 数值比较
        assert_that!(10, gt(5));
        assert_that!(3.14, close_to(3.14159, 0.01));
        
        // 字符串匹配
        assert_that!("hello world", matches_regex(r"hello"));
        assert_that!("12345", matches_regex(r"\d+"));
        
        // 文件系统测试
        let test_file = Path::new("Cargo.toml");
        assert_that!(test_file, file_exists());
    }
    
    #[test]
    fn test_collections() {
        let numbers = vec![1, 2, 3, 4, 5];
        
        // 集合包含测试
        assert_that!(&numbers, contains(3));
        assert_that!(&numbers, contains(vec![2, 4]));
        assert_that!(&numbers, len(5));
        
        // 精确匹配
        assert_that!(&numbers, contains(vec![1, 2, 3, 4, 5]).exactly());
    }
    
    #[test]
    fn test_options_results() {
        // Option测试
        let some_value: Option<i32> = Some(42);
        assert_that!(some_value, some());
        assert_that!(some_value, has(42));
        
        // Result测试
        let ok_result: Result<&str极客时间, &str> = Ok("success");
        assert_that!(ok_result, ok());
        assert_that!(ok_result, has("success"));
        
        let err_result: Result<(), &str> = Err("failure");
        assert_that!(err_result, err());
    }
    
    #[test]
    fn test_complex_conditions() {
        // 复合条件测试
        assert_that!(
            15,
            all!(
                gt(10),
                lt(20),
                is(15)
            )
        );
        
        // 使用or条件
        assert_that!(
            "test string",
            any!(
                matches_regex(r"test"),
                matches_regex(r"string")
            )
        );
    }
}

许可证

Hamcrest2采用以下许可证:

  • Apache License, Version 2.0
  • MIT license

您可以选择其中任何一种许可证使用。


1 回复

Rust测试断言库hamcrest2使用指南

hamcrest2是一个Rust测试断言库,提供了丰富的匹配器(matchers)来增强单元测试和集成测试的断言功能。它基于Hamcrest模式,为测试提供了更灵活和表达性强的断言方式。

安装

在Cargo.toml中添加依赖:

[dev-dependencies]
hamcrest2 = "0.3.0"

基本使用

use hamcrest2::prelude::*;

#[test]
fn test_basic_assertions() {
    // 等于匹配
    assert_that!(42, eq(42));
    
    // 不等于匹配
    assert_that!("hello", ne("world"));
    
    // 大于匹配
    assert_that!(10, gt(5));
}

常用匹配器

值匹配

#[test]
fn test_value_matchers() {
    let value = 42;
    
    // 等于
    assert_that!(value, eq(42));
    
    // 不等于
    assert_that!(value, ne(0));
    
    // 大于
    assert_that!(value, gt(40));
    
    // 小于
    assert_that!(value, lt(50));
    
    // 在范围内
    assert_that!(value, is(within(40..=45)));
}

字符串匹配

#[test]
fn test_string_matchers() {
    let text = "Rust programming language";
    
    // 包含子串
    assert_that!(text, contains_str("program"));
    
    // 以...开头
    assert_that!(text, starts_with("Rust"));
    
    // 以...结尾
    assert_that!(text, ends_with("language"));
    
    // 正则匹配
    assert_that!(text, matches_regex(r"Rust .* language"));
}

集合匹配

#[test]
fn test_collection_matchers() {
    let vec = vec![1, 2, 3, 4, 5];
    
    // 集合长度
    assert_that!(vec, has_length(5));
    
    // 包含元素
    assert_that!(vec, contains(3));
    
    // 不包含元素
    assert_that!(vec, not(contains(6)));
    
    // 所有元素满足条件
    assert_that!(vec, every_item(gt(0)));
}

组合匹配器

#[test]
fn test_combinators() {
    let value = 15;
    
    // 逻辑与
    assert_that!(value, all_of![gt(10), lt(20)]);
    
    // 逻辑或
    assert_that!(value, any_of![eq(10), eq(15)]);
    
    // 逻辑非
    assert_that!(value, not(eq(0)));
}

Option和Result匹配

#[test]
fn test_option_result_matchers() {
    // Option匹配
    let some_value: Option<i32> = Some(42);
    assert_that!(some_value, is_some());
    assert_that!(some_value, is_some_with(eq(42)));
    
    let none_value: Option<i32> = None;
    assert_that!(none_value, is_none());
    
    // Result匹配
    let ok_result: Result<i32, &str> = Ok(42);
    assert_that!(ok_result, is_ok());
    assert_that!(ok_result, is_ok_with(eq(42)));
    
    let err_result: Result<i32, &str> = Err("error");
    assert_极速running("error"));
}

自定义匹配器

你可以创建自己的匹配器:

use hamcrest2::core::*;

fn is_even() -> Matcher<'static, i32> {
    Matcher::new(
        |x: &i32| x % 2 == 0,
        "even number",
        "odd number",
    )
}

#[test]
fn test_custom_matcher() {
    assert_that!(42, is_even());
    assert_that!(43, not(is_even()));
}

错误信息

当断言失败时,hamcrest2会提供详细的错误信息:

#[test]
fn test_error_messages() {
    let vec = vec![1, 2, 3];
    // 失败时会显示:Expected: collection containing <4> but: was <[1, 2, 3]>
    assert_that!(vec, contains(4));
}

hamcrest2通过丰富的匹配器和组合能力,可以编写出更清晰、更具表达力的测试断言,特别适合复杂条件的测试场景。

回到顶部