neysofu/reltester
GitHub: neysofu/reltester
Reltester 是一个 Rust 测试工具库,用于自动验证自定义实现的比较、哈希和迭代器相关 trait 是否满足其数学不变性约束。
Stars: 20 | Forks: 2
# Reltester
[](https://github.com/neysofu/reltester/blob/main/LICENSE.txt) [](https://docs.rs/reltester/latest/reltester/) [](https://github.com/neysofu/reltester/actions) [](https://crates.io/crates/reltester) [](https://github.com/neysofu/reltester/blob/main/rust-toolchain.toml)
**Rel**ation **tester** 是一个小型测试实用工具,用于自动检查 `[Partial]Eq`、`[Partial]Ord`、`Hash` 和 `[DoubleEnded|Fused]Iterator` trait 实现的正确性。它与 [`quickcheck`](https://github.com/BurntSushi/quickcheck) 或其他基于属性的测试框架结合使用时最为有效。
*前往[文档](https://docs.rs/reltester/latest/reltester/)!*
## 动机
想象这样一个场景:你有一个类型 `Foo`,并且为它自定义实现了 `PartialEq`、`Eq`、`PartialOrd` 或 `Ord`。这里的“自定义”是指手动编写而不是使用派生(derive)。仅凭 Rust 编译器无法验证这些实现的正确性,因此要由你(程序员)来确保你所实现的特定[二元关系](https://en.wikipedia.org/wiki/Binary_relation)满足某些不变性。例如,如果你为 `Foo` 实现了 `PartialEq`,你必须保证 `foo1 == foo2` 意味着 `foo2 == foo1`(*对称性*)。
`Hash` 和 `Iterator` 等其他 trait 同样要求满足若干不变性——有些非常直观,而[另一些](https://doc.rust-lang.org/std/hash/trait.Hash.html#prefix-collisions)则不然。对于 `std::iter` 系列 trait,实现得不够完美时非常容易引入 off-by-one(差一)错误[^1][^2][^3][^4] 等问题。
我们的理念是:与其每次在代码库中手动实现这些 trait 时都要在脑海中牢记这些不变性,不如在你的测试套件中加入 Reltester 检查,从而对你的实现的正确性拥有更高的信心。
## 使用方法
1. 编写一些测试,生成你想要测试的类型的随机值。你可以手动执行此操作,也可以使用诸如 [`quickcheck`](https://github.com/BurntSushi/quickcheck) 和 [`proptest`](https://github.com/proptest-rs/proptest) 之类的 crate。虽然对静态、非随机值调用检查器也是可行的,但在捕获错误方面效果较差。
2. 根据你的类型所实现的 trait,调用相应的检查器:
- `reltester::eq` 用于 `Eq`;
- `reltester::ord` 用于 `Ord`;
- `reltester::partial_eq` 用于 `PartialEq`;
- `reltester::partial_ord` 用于 `PartialOrd`;
- `reltester::hash` 用于 `Hash`;
- `reltester::iterator` 用于 `Iterator`;
- `reltester::fused_iterator` 用于 `FusedIterator`;
- `reltester::double_ended_iterator` 用于 `DoubleEndedIterator`;
其中一些函数接收多个(两个或三个)相同类型的值。这是因为测试某些不变性最多需要三个值。
请查阅文档以获取更多信息。如果你无法满足主函数的类型约束,可以使用 `reltester::invariants` 模块进行更细致的检查。
## 示例
### `f32` (`PartialEq`, `PartialOrd`)
```
use reltester;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn test_f32(a: f32, b: f32, c: f32) -> bool {
// Let's check if `f32` implements `PartialEq` and `PartialOrd` correctly
// (spoiler: it does).
reltester::partial_eq(&a, &b, &c).is_ok()
&& reltester::partial_ord(&a, &b, &c).is_ok()
}
```
### `u32` (`Hash`)
```
use reltester;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn test_u32(a: u32, b: u32) -> bool {
// Unlike `f32`, `u32` implements both `Eq` and `Hash`, which allows us to
// test `Hash` invariants.
reltester::hash(&a, &b).is_ok()
}
```
### `Vec` (`DoubleEndedIterator`, `FusedIterator`, `Iterator`)
```
use reltester;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn test_vec_u32(nums: Vec) -> bool {
// `Iterator` is implied and checked by both `DoubleEndedIterator` and
// `FusedIterator`.
reltester::double_ended_iterator(nums.iter()).is_ok()
&& reltester::fused_iterator(nums.iter()).is_ok()
}
```
## 法律声明
Reltester 基于 MIT 许可证发布。
## 外部参考资料与脚注
标签:Rust, 单元测试, 可视化界面, 属性测试, 测试工具, 网络流量审计, 通知系统