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 许可证 授权