4个稳定版本
| 1.3.0 | 2021年10月2日 | 
|---|---|
| 1.2.0 | 2021年10月2日 | 
| 1.1.0 | 2021年10月2日 | 
| 1.0.0 | 2021年9月30日 | 
#645 in 测试
31KB
549 行
rassert
🦀 ❓ ❗
Rust的流畅、易于扩展的测试断言
功能
- 流畅的期望。在编写测试时,我们追求清晰和可读性。rassert通过提供流畅的期望接口,允许将断言尽可能接近自然语言。请参阅编写断言。
- 人性化的失败。在失败时,rassert会显示测试表达式、位置、实际值和预期值/条件,以帮助调试。
- 模式匹配支持。使用expect_matches!宏来检查表达式是否匹配某些模式,例如expect_matches!(result, Ok(..))。
- 惰性评估和软评估。断言是惰性评估的,只有当通过conclude_panic或conclude_result函数调用关闭期望链时才会进行评估。在软模式(通过soft()调用开启)下,期望链评估不会在第一个失败时停止。请参阅编写断言。
- 易于扩展。通过自定义期望轻松扩展rassert。请参阅自定义期望。
快速开始
首先,将rassert添加到您的Cargo.toml文件中的dev-dependencies部分
[dev-dependencies]
rassert = "1"
然后在测试中简单地导入prelude模块
#[cfg(test)]
mod tests {
    use rassert::prelude::*;
    #[test]
    fn rassert_works() {
        expect!(&true)
            .not()
            .to_be_false()
            .and()
            .to_equal(true)
            .conclude_panic();
    }
}
编写断言
期望链
在rassert中,可以以期望链的形式编写断言。此类链允许对同一表达式编写多个期望。有两种所谓的入口点,可以开始链
- expect!(expression)- 创建一个新的链,断言提供的expression。
 
- 创建一个新的链,断言提供的
- expect_matches!(expression,pattern)- 从提供的 expression创建一个新的链,并自动向链中添加一个期望,断言expression与pattern匹配。
 
- 从提供的 
一旦启动了链,就可以在链上调用期望,如下所示
let v = vec![10];
expect!(&v)
    .to_be_non_empty()
    .and()
    .to_contain(10);
注意,and() 调用不是强制的,它只用于提高可读性。
结束链
由于 rassert 惰性评估期望,上述链将不执行任何操作。链只有在结束时才会断言指定的期望
// Will panic on a failed expectation.
expect!(&true)
    .to_be(false)
    .conclude_panic();
// Will return Result<(), String> containing the error
// message on failure.
let res = expect!(&true)
  .to_be(false)
  .conclude_result();
软模式
可以通过在结束链之前调用 soft() 将链置于软模式
let v = vec![10];
expect!(&v)
    .to_contain(15)
    .and()
    .to_contain(20)
    .soft()
    .conclude_panic();
软链在第一次失败时不会恐慌/返回,而是会运行每个断言,并呈现每个发生的失败的合并报告。
否定期望
可以使用 not() 函数否定一个随后的期望
expect!(&true)
    .not()
    .to_be_false()
    .conclude_panic();
如果想要否定更多的期望,那么需要再次应用 not()。
可用期望
- T- 到
 
- T,其中- T: Debug + PartialEq- to_equal,- to_be
- to_not_equal,- to_not_be
 
- 布尔值- to_be_true
- to_be_false
 
- Option<T>- to_be_some
- to_be_none
 
- Option<T>,其中- T: Debug + PartialEq- to_contain
 
- 结果<T, E>- to_be_ok
- to_be_err
 
- Result<T, E>,其中- T: Debug + PartialEq- to_be_ok_with
 
- Vec<T>- to_have_length
- to_be_empty
- to_be_non_empty
 
- Vec<T>,其中- T: Debug + PartialEq- to_contain
 
自定义期望
自定义期望可以通过在 rassert 提供的 ExpectationChain 类型上编写 扩展特质 来编写。以下通过一个示例展示了如何编写自定义期望。
假设,我们想要编写一个针对自定义结构体 Pizza 的期望
#[derive(Debug)]
pub struct Pizza {
    pub flavor: String
}
在 rassert 中,期望实际上是遵循某种类似 命令模式 的结构体。给定,我们想要检查我们有什么口味的比萨,我们可以创建如下所示的内容
use rassert::Expectation;
struct ExpectPizzaFlavor {
    expected_flavor: String
}
impl Expectation<Pizza> for ExpectPizzaFlavor {
    fn test(&self, actual: &Pizza) -> bool {
        actual.flavor.eq(&self.expected_flavor)
    }
    fn message(&self, expression: &str, actual: &Pizza) -> String {
        format!("Expected {:?}\n  to have flavor {}\n  but had {}.", expression, self.expected_flavor, actual.flavor)
    }
}
实现 Expectation 特质需要两个函数
- fn(&self,actual) -> bool- 实际的断言,在成功时返回 true,否则返回 false。参数 actual对应于链中正在测试的值。
 
- 实际的断言,在成功时返回 true,否则返回 false。参数 
- fn(&self, expression, actual)-> String- 当期望失败时显示的消息。expression参数是expect!/expect_matches!宏的字符串化表达式参数。
 
- 当期望失败时显示的消息。
一旦我们写好了期望结构,我们就可以最终扩展 ExpectationChain 类型
use rassert::ExpectationChain;
pub trait PizzaExpectationsExt<'a> {
    fn to_have_flavor(self, expected: &str) -> ExpectationChain<'a, Pizza>;
}
impl<'a> PizzaExpectationsExt for ExpectationChain<'a, Pizza> {
    fn to_have_flavor(self, expected: &str) -> ExpectationChain<'a, Pizza> {
        self.expecting(ExpectPizzaFlavor {
            expected_flavor: expected.to_owned()
        })
    }
}
上述代码片段最重要的部分如下
- 扩展特质必须接受一个泛型生命周期参数,并在从期望函数返回的 ExpectationChain类型中使用它。此参数对应于链中保持的不变引用的生命周期。然后,此引用指向实际测试的值。
- 期望函数必须接受 self,因为期望链是 消耗构建器。
- 期望函数可以使用 expecting()函数通过新的期望扩展链。此函数接受一个期望,当完成链时将执行。期望的字段可以用来参数化实际的断言。
然后,使用上述期望就像
let pizza = Pizza {
  flavor: "Hawaii".to_owned(),
};
expect!(&pizza)
  .to_have_flavor("Margherita")
  .conclude_panic();
src/expectations 目录中的内置期望也使用上述功能,因此它们是编写自定义期望的绝佳起点。
许可证
根据 MIT 许可证 授权