neysofu/reltester

GitHub: neysofu/reltester

Reltester 是一个 Rust 测试工具库,用于自动验证自定义实现的比较、哈希和迭代器相关 trait 是否满足其数学不变性约束。

Stars: 20 | Forks: 2

# Reltester [![Crates.io](https://img.shields.io/crates/l/reltester)](https://github.com/neysofu/reltester/blob/main/LICENSE.txt) [![docs.rs](https://img.shields.io/docsrs/reltester)](https://docs.rs/reltester/latest/reltester/) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/neysofu/reltester/ci.yml)](https://github.com/neysofu/reltester/actions) [![Crates.io](https://img.shields.io/crates/v/reltester)](https://crates.io/crates/reltester) [![min-rustc](https://img.shields.io/badge/min--rustc-1.56-blue)](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, 单元测试, 可视化界面, 属性测试, 测试工具, 网络流量审计, 通知系统