你需要知道的32个Rust库 - 4

584次阅读  |  发布于1年以前

19,Getset

以前的Java程序员会喜欢这个crate。Getset crate包含用于生成getter和setter方法的过程性宏。

use getset::{CopyGetters, Getters, MutGetters, Setters};

#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
pub struct Foo<T>
where
    T: Copy + Clone + Default,
{
    #[getset(get, set, get_mut)]
    private: T,

    #[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
    public: T,
}

fn main() {
    let mut foo = Foo::default();
    foo.set_private(1);
    (*foo.private_mut()) += 1;
    assert_eq!(*foo.private(), 2);
}

20,Mockall

Mockall crate为(几乎所有)Trait和结构体提供了自动生成的模拟对象,这些对象可以在单元测试中使用,而不是使用原始类型的对象,这可以使编写高级单元测试或测试复杂的边缘情况变得更容易。

#[cfg(test)]
use mockall::{automock, predicate::*};

#[cfg_attr(test, automock)]
trait CalcTrait {
    fn foo(&self, x: u32) -> u32;
}

fn calculation(calc: impl CalcTrait, x: u32) -> u32 {
    calc.foo(x)
}

#[test]
fn test() {
    let mut mock = MockCalcTrait::new();
    mock.expect_foo().with(eq(4)).times(1).returning(|x| x + 1);

    assert_eq!(5, calculation(mock, 4));
}

可以使用#[automock]属性宏自动生成模拟对象。但是,它有其局限性,因此有时必须使用过程宏mock!手动实现模拟对象。

21,QuickCheck

QuickCheck是一个基于属性的测试框架。它允许测试带有大量任意输入数据的代码。如果发现了错误,它会自动找到最小的测试用例来重现错误。

#[cfg(test)]
mod tests {
    fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
        let mut rev = vec!();
        for x in xs {
            rev.insert(0, x.clone())
        }
        rev
    }

    #[quickcheck]
    fn double_reversal_is_identity(xs: Vec<isize>) -> bool {
        xs == reverse(&reverse(&xs))
    }
}

22,Proptest

和quickcheck一样,protest也是一个基于属性的测试框架。然而,与quickcheck相比,它可以更灵活的生成输入数据,尽管对于复杂的数据,它可能比quickcheck运行时间长得多。

proptest! {
    #[test]
    fn doesnt_crash(s in "\\PC*") {
        parse_date(&s);
    }

    #[test]
    fn parses_date_back_to_original(y in 0u32..10000,
                                    m in 1u32..13,
                                    d in 1u32..32)
    {
        let result = parse_date(&format!("{:04}-{:02}-{:02}", y, m, d)).unwrap();

        prop_assert_eq!((y, m, d), result);
    }
}

23,Heck

Heck crate用于将文本转换为各种常用的变量命名样式,如CamelCase、snake_case等。

use heck::ToShoutyKebabCase;

#[test]
fn test() {
    assert_eq!("i am very angry!".to_shouty_kebab_case(), "I-AM-VERY-ANGRY");
}

24,num_cpus

num_cpus crate 用于确定物理CPU内核的数量或可以在系统上有效执行的并行任务的数量。

fn main() {
    println!("Logical CPUs: {}", num_cpus::get());
    println!("Physical CPUs: {}", num_cpus::get_physical());
}

25,Humantime

Humantime crate 以人类可读的格式为std::time::{Duration, SystemTime}提供了格式化器和解析器。它还通过humantime-serde crate与serde集成。

例如,可以在应用程序/服务配置中以可读格式指定Duration值,而不是在变量名中使用度量单位,从而减少错误的可能性:

use serde::{Deserialize, Serialize};
use std::time::Duration;

#[test]
fn format() {
    let duration = Duration::new(9420, 0);
    let as_str = "2h 37m";
    assert_eq!(humantime::format_duration(duration).to_string(), as_str);
    assert_eq!(humantime::parse_duration(as_str), Ok(duration));
}

#[derive(Serialize, Deserialize)]
struct Foo {
    #[serde(with = "humantime_serde")]
    timeout: Duration,
}

#[test]
fn serde() {
    let input = r#" { "timeout": "3 days 1hour 12min 5s" } "#;
    let foo: Foo = serde_json::from_str(input).unwrap();
    assert_eq!(foo.timeout, Duration::new(263525, 0));
}

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8