5 个版本 (破坏性更新)
使用旧的 Rust 2015
0.11.0 | 2022年12月3日 |
---|---|
0.10.0 | 2022年10月28日 |
0.9.0 | 2022年4月15日 |
0.8.0 | 2021年10月31日 |
0.7.0 | 2021年9月18日 |
#165 在 Rust 模式 中
每月 14,851 次下载
被 58 个 包使用(53 个直接使用)
140KB
2.5K SLoC
这是对未维护的包 spectral 的分支。Spectral 五年未变,但仍然非常可用,这个分支的目的是在不破坏现有 API 的情况下添加新的断言功能。
speculoos
Rust 的流畅式测试断言。
受 Google Truth 和其他流畅断言框架的影响。
用法
将此添加到您的 Cargo.toml
[dependencies]
speculoos = "0.9.0"
要快速开始使用断言,只需在测试模块中使用 prelude
模块
use speculoos::prelude::*;
概述
Speculoos 允许您以流畅的方式编写断言,通过区分您要测试的内容、测试对象以及断言方式。
简单断言
例如,要测试生成的值是否等于预期值,您将编写
assert_that(&1).is_equal_to(1);
或者测试 Vec 是否包含一定数量的元素
let test_vec = vec![1,2,3];
assert_that(&test_vec).has_length(3);
可用于断言的方法取决于测试的类型以及实现的特性。
如以下所述,建议使用 assert_that!
宏的形式来为失败的断言提供正确的文件和行号。
失败消息
对于失败的断言,通常的 panic 消息遵循以下格式
expected: <2>
but was: <1>
为了对 panic 消息添加额外的说明,您也可以通过调用 asserting(...)
函数而不是 assert_that(...)
asserting(&"test condition").that(&1).is_equal_to(&2);
这将产生
test condition:
expected: <2>
but was: <1>
使用 assert_that!
宏的形式将为您提供失败的断言的文件和行号
expected: vec to have length <2>
but was: <1>
at location: tests/parser.rs:112
命名主题
为了使您的主题实际内容更明显,您可以在assert_that
(或asserting
)之后调用.named(...)
,这将打印出提供的&str
作为主题名称,如果断言失败。
assert_that(&thing.attributes).named(&"thing attributes").has_length(2);
在失败时,这将显示
for subject [thing attributes]
expected: vec to have length <2>
but was: <1>
映射值
如果您想对结构体中包含的值进行断言,您可以使用闭包调用map(...)
,这将根据闭包的返回值创建一个新的Spec
。然后您可以对映射值调用任何适用的断言。
let test_struct = TestStruct { value: 5 };
assert_that(&test_struct).map(|val| &val.value).is_equal_to(5);
宏定义
如果您将#[macro_use]
添加到extern crate
声明中,您还可以使用assert_that
和asserting
的宏形式。
assert_that!(test_vec).has_length(5)
这允许您在不需要故意将其转换为引用的情况下传递要测试的主题。然而,为了保持一致性,您也可以在宏中使用故意引用。
assert_that!(&test_vec).has_length(5)
此外,这将为您提供失败断言的文件和行号(而不是仅内部speculoos恐慌位置)。
断言(基本)
注意:每个断言的描述和示例在下面的readme中进一步说明。
通用
is_equal_to
is_not_equal_to
matches
布尔值
is_true
is_false
数字
is_less_than
is_less_than_or_equal_to
is_greater_than
is_greater_than_or_equal_to
浮点数(可选)
is_close_to
选项
is_some -> (返回一个新的包含Option值的Spec)
is_none
contains_value
路径
exists
does_not_exist
is_a_file
is_a_directory
has_file_name
结果
is_ok -> (返回一个新的包含Ok值的Spec)
is_err -> (返回一个新的包含Err值的Spec)
is_ok_containing
is_err_containing
字符串
starts_with
ends_with
contains
is_empty
向量
has_length
is_empty
HashMaps
has_length
is_empty
contains_key -> (返回一个新的包含键值的Spec)
does_not_contain_key
contains_entry
does_not_contain_entry
HashSet
has_length
is_empty
contains (从迭代器中)
does_not_contain (从迭代器中)
contains_all_of (从迭代器中)
IntoIterator/Iterator
contains
does_not_contain
contains_all_of
mapped_contains
equals_iterator
IntoIterator
matching_contains
可选功能
Num Crate
num
crate用于Float
断言。此功能默认启用,但如果您不想依赖num
,则可以简单地禁用它。
断言(详细)
一般而言,任何测试的类型通常都需要实现至少Debug
。其他断言将具有不同的边界。
通用
is_equal_to
断言主题和预期值相等。主题类型必须实现 PartialEq
。
示例
assert_that(&"hello").is_equal_to(&"hello");
失败信息
expected: <2>
but was: <1>
is_not_equal_to
断言主题和预期值不相等。主题类型必须实现 PartialEq
。
示例
assert_that(&"hello").is_not_equal_to(&"hello");
失败信息
expected: <1> not equal to <1>
but was: equal
matches
接受一个接受主题类型并返回布尔值的函数。返回false将导致断言失败。
注意:生成的panic消息只会说明实际值。建议您编写自己的断言而不是依赖于这个。
示例
assert_that(&"Hello").matches(|val| val.eq(&"Hello"));
失败信息
expectation failed for value <"Hello">
布尔值
is_true
断言主题为true。主题类型必须是 bool
。
示例
assert_that(&true).is_true();
失败信息
expected: bool to be <true>
but was: <false>
is_false
断言主题为false。主题类型必须是 bool
。
示例
assert_that(&false).is_false();
失败信息
expected: bool to be <false>
but was: <true>
数字
is_less_than
断言主题值小于预期值。主题类型必须实现 PartialOrd
。
示例
assert_that(&1).is_less_than(&2);
失败信息
expected: value less than <2>
but was: <3>
is_less_than_or_equal_to
断言主题小于或等于预期值。主题类型必须实现 PartialOrd
。
示例
assert_that(&2).is_less_than_or_equal_to(&2);
失败信息
expected: value less than or equal to <2>
but was: <3>
is_greater_than
断言主题大于预期值。主题类型必须实现 PartialOrd
。
示例
assert_that(&2).is_greater_than(&1);
失败信息
expected: value greater than <3>
but was: <2>
is_greater_than_or_equal_to
断言主题大于或等于预期值。主题类型必须实现 PartialOrd
。
示例
assert_that(&2).is_greater_than_or_equal_to(&1);
失败信息
expected: value greater than or equal to <3>
but was: <2>
浮点数(可选)
is_close_to
断言主题值在指定的公差内接近预期值。主题类型必须实现 Float
和 Debug
。
示例
assert_that(&2.0f64).is_close_to(2.0f64, 0.01f64);
失败信息
expected: float close to <1> (tolerance of <0.01>)
but was: <2>
选项
is_some -> (返回一个新的包含Option值的Spec)
断言主题是 Some
。主题类型必须是一个 Option
。
如果它是 Some
,则返回一个新的包含解包值的 Spec
。
示例
assert_that(&Some(1)).is_some();
链式调用
assert_that(&option).is_some().is_equal_to(&"Hello");
失败信息
expected: option[some]
but was: option[none]
is_none
断言主题是 None
。值类型必须是一个 Option
。
示例
assert_that(&Option::None::<String>).is_none();
失败信息
expected: option[none]
but was: option<"Hello">
contains_value
断言主题是一个包含预期值的 Some
。主题类型必须是一个 Option
。
示例
assert_that(&Some(1)).contains_value(&1);
失败信息
expected: option to contain <"Hi">
but was: <"Hello">
路径
exists
断言主题 Path
或 PathBuf
指向一个现有位置。
示例
assert_that(&Path::new("/tmp/file")).exists();
失败信息
expected: Path of <"/tmp/file"> to exist
but was: a non-existent Path
does_not_exist
断言主题 Path
或 PathBuf
不指向一个现有位置。
示例
assert_that(&Path::new("/tmp/file")).does_not_exist();
失败信息
expected: Path of <"/tmp/file"> to not exist
but was: a resolvable Path
is_a_file
断言主题 Path
或 PathBuf
指向一个现有文件。
示例
assert_that(&Path::new("/tmp/file")).is_a_file();
失败信息
expected: Path of <"/tmp/file"> to be a file
but was: not a resolvable file
is_a_directory
断言主题 Path
或 PathBuf
指向一个现有目录。
示例
assert_that(&Path::new("/tmp/dir/")).is_a_directory();
失败信息
expected: Path of <"/tmp/dir/"> to be a directory
but was: not a resolvable directory
has_file_name
断言主题 Path
或 PathBuf
拥有预期的文件名。
示例
assert_that(&Path::new("/tmp/file")).has_file_name(&"file");
失败信息
expected: Path with file name of <pom.xml>
but was: <Cargo.toml>
结果
is_ok -> (返回一个新的包含Ok值的Spec)
断言主题是 Ok
。值类型必须是一个 Result
。
如果它是 Ok
,则返回一个新的包含解包值的 Spec
。
示例
assert_that(&Result::Ok::<usize, usize>(1)).is_ok();
链式调用
let result: Result<&str, &str> = Ok("Hello");
assert_that(&result).is_ok().is_equal_to(&"Hello");
失败信息
expected: result[ok]
but was: result[error]<"Oh no">
is_err -> (返回一个新的包含Err值的Spec)
断言主题是 Err
。值类型必须是一个 Result
。
如果它是 Err
,则返回一个新的包含解包值的 Spec
。
注意:这曾经被称为 is_error
,但已被重命名为符合标准Rust命名。
示例
assert_that(&Result::Err::<usize, usize>(1)).is_err();
链式调用
let result: Result<&str, &str> = Err("Hello");
assert_that(&result).is_err().is_equal_to(&"Hello");
失败信息
expected: result[error]
but was: result[ok]<"Hello">
is_ok_containing
断言主题是一个包含预期值的 Ok
结果。主题类型必须是 Result
。
示例
assert_that(&Result::Ok::<usize, usize>(1)).is_ok_containing(&1);
失败信息
expected: Result[ok] containing <"Hi">
but was: Result[ok] containing <"Hello">
is_err_containing
断言主题是一个包含预期值的 Err
结果。主题类型必须是 Result
。
示例
assert_that(&Result::Err::<usize, usize>(1)).is_err_containing(&1);
失败信息
expected: Result[err] containing <"Oh no">
but was: Result[err] containing <"Whoops">
字符串
starts_with
断言主题 &str
或 String
以提供的 &str
开头。
示例
assert_that(&"Hello").starts_with(&"H");
失败信息
expected: string starting with <"A">
but was: <"Hello">
ends_with
断言主题 &str
或 String
以提供的 &str
结尾。
示例
assert_that(&"Hello").ends_with(&"o");
失败信息
expected: string ending with <"A">
but was: <"Hello">
contains
断言主题 &str
或 String
包含提供的 &str
。
示例
assert_that(&"Hello").contains(&"e");
does_not_contain
断言主题 &str
或 String
不包含提供的 &str
。
示例
assert_that(&"Hello").does_not_contain(&"Bonjour");
失败信息
expected: string containing <"A">
but was: <"Hello">
is_empty
断言主题 &str
或 String
表示一个空字符串。
示例
assert_that(&"").is_empty();
失败信息
expected: an empty string
but was: <"Hello">
向量
has_length
断言主题向量的长度等于提供的长度。主题类型必须是 Vec
。
示例
assert_that(&vec![1, 2, 3, 4]).has_length(4);
失败信息
expected: vec to have length <1>
but was: <3>
is_empty
断言主题向量是空的。主题类型必须是 Vec
。
示例
let test_vec: Vec<u8> = vec![];
assert_that(&test_vec).is_empty();
失败信息
expected: an empty vec
but was: a vec with length <1>
HashMaps
has_length
断言主题哈希表的长度等于提供的长度。主题类型必须是 HashMap
。
示例
let mut test_map = HashMap::new();
test_map.insert(1, 1);
test_map.insert(2, 2);
assert_that(&test_map).has_length(2);
失败信息
expected: hashmap to have length <1>
but was: <2>
is_empty
断言主题哈希表是空的。主题类型必须是 HashMap
。
示例
let test_map: HashMap<u8, u8> = HashMap::new();
assert_that(&test_map).is_empty();
失败信息
expected: an empty hashmap
but was: a hashmap with length <1>
contains_key -> (返回一个新的包含键值的Spec)
断言主题哈希表包含预期的键。主题类型必须是 HashMap
。
如果键存在,这将返回一个包含关联值的新 Spec
。
示例
let mut test_map = HashMap::new();
test_map.insert("hello", "hi");
assert_that(&test_map).contains_key(&"hello");
链式调用
let mut test_map = HashMap::new();
test_map.insert("hello", "hi");
assert_that(&test_map).contains_key(&"hello").is_equal_to(&"hi");
失败信息
expected: hashmap to contain key <"hello">
but was: <["hey", "hi"]>
does_not_contain_key
断言主题哈希表不包含提供的键。主题类型必须是 HashMap
。
示例
let mut test_map = HashMap::new();
test_map.insert("hello", "hi");
assert_that(&test_map).does_not_contain_key(&"hey");
失败信息
expected: hashmap to not contain key <"hello">
but was: present in hashmap
contains_entry
断言主题哈希表包含预期的键和值。主题类型必须是 HashMap
。
示例
let mut test_map = HashMap::new();
test_map.insert("hello", "hi");
assert_that(&test_map).contains_entry(&"hello", &"hi");
失败信息
expected: hashmap containing key <"hi"> with value <"hey">
but was: key <"hi"> with value <"hello"> instead
does_not_contain_entry
断言主题哈希表不包含提供的键和值。主题类型必须是 HashMap
。
示例
let mut test_map = HashMap::new();
test_map.insert("hello", "hi");
assert_that(&test_map).does_not_contain_entry(&"hello", &"hey");
失败信息
expected: hashmap to not contain key <"hello"> with value <"hi">
but was: present in hashmap
HashSet
has_length
断言主题哈希表的长度等于提供的长度。主题类型必须是 HashSet
。
示例
let mut test_map = HashSet::new();
test_map.insert(1);
test_map.insert(2);
assert_that(&test_map).has_length(2);
失败信息
expected: hashset to have length <1>
but was: <2>
is_empty
断言主题哈希表是空的。主题类型必须是 HashSet
。
示例
let test_map: HashSet<u8> = HashSet::new();
assert_that(&test_map).is_empty();
失败信息
expected: an empty hashset
but was: a hashset with length <1>
基于迭代器的断言
由于哈希表实现了迭代器,以下所有的迭代器断言也适用(例如 contains & does_not_contain)
IntoIterator/Iterator
contains
断言主题包含提供的值。主题必须实现 IntoIterator
或 Iterator
,并且包含的类型必须实现 PartialEq
和 Debug
。
示例
let test_vec = vec![1,2,3];
assert_that(&test_vec).contains(&2);
失败信息
expected: iterator to contain <1>
but was: <[5, 6]>
does_not_contain
断言主题不包含提供的数据。主题必须实现 IntoIterator
或 Iterator
,并且包含的类型必须实现 PartialEq
和 Debug
。
示例
let test_vec = vec![1,2,3];
assert_that(&test_vec).does_not_contain(&4);
失败信息
expected: iterator to not contain <1>
but was: <[1, 2]>
contains_all_of
断言主题包含所有提供的数据。主题必须实现 IntoIterator
或 Iterator
,并且包含的类型必须实现 PartialEq
和 Debug
。
示例
let test_vec = vec![1, 2, 3];
assert_that(&test_vec.iter()).contains_all_of(&vec![&2, &3]);
失败信息
expected: iterator to contain items <[1, 6]>
but was: <[1, 2, 3]>
mapped_contains
在断言映射后的主题包含提供的数据之前,映射主题的值。主题必须实现 IntoIterator,映射值的类型必须实现 PartialEq
。
注意:恐慌信息将引用映射值,而不是原始主题中的值。
示例
#[derive(PartialEq, Debug)]
struct Simple {
pub val: usize,
}
...
assert_that(&vec![Simple { val: 1 }, Simple { val: 2 } ]).mapped_contains(|x| &x.val, &2);
失败信息
expected: iterator to contain <5>
but was: <[1, 2, 3]>
equals_iterator
断言主题等于提供的迭代器。主题必须实现 IntoIterator
或 Iterator
,包含的类型必须实现 PartialEq
和 Debug
,并且期望值必须实现 Iterator 和 Clone。
示例
let expected_vec = vec![1,2,3];
let test_vec = vec![1,2,3];
assert_that(&test_vec).equals_iterator(&expected_vec.iter());
失败信息
expected: Iterator item of <4> (read <[1, 2]>)
but was: Iterator item of <3> (read <[1, 2]>)
IntoIterator
matching_contains
使用提供的函数通过匹配项断言主题包含匹配项。主题必须实现 IntoIterator
,并且包含的类型必须实现 Debug
。
示例
let mut test_into_iter = LinkedList::new();
test_into_iter.push_back(TestEnum::Bad);
test_into_iter.push_back(TestEnum::Good);
test_into_iter.push_back(TestEnum::Bad);
assert_that(&test_into_iter).matching_contains(|val| {
match val {
&TestEnum::Good => true,
_ => false
}
});
失败信息
expectation failed for iterator with values <[Bad, Bad, Bad]>
工作原理
Spec
结构体实现了一系列不同的有界特质,这些特质基于界限类型提供断言。
作为一个单一的例子,长度断言由 VecAssertions
特质提供
pub trait VecAssertions {
fn has_length(self, expected: usize);
}
然后由 Spec 实现
impl<'s, T> VecAssertions for Spec<'s, Vec<T>> {
fn has_length(self, expected: usize) {
...
}
}
自然地,特质需要在应用之前通过 use
包含,但为了避免过多的 use
语句,有一个 prelude
模块重新导出常用的断言特质。
创建自己的
要创建自己的断言,只需创建一个新的特质,包含你的断言方法,并针对它实现 Spec。
要使断言失败,在你的断言方法中使用 from_spec(...)
创建一个新的 AssertionFailure
结构体,并在其中传递 self
。
AssertionFailure
还实现了构建器方法 with_expected(...)
、with_actual(...)
和 fail(...)
,这提供了失败测试所需的通常的消息格式。如果您需要更大的对失败消息的控制,您可以调用 fail_with_message(...)
,这将直接打印提供的信息。
在两种情况下,使用 asserting(...)
提供的任何描述都将始终附加到恐慌信息。
例如,要创建一个断言,表示 Vec
的长度至少是一个特定的值
trait VecAtLeastLength {
fn has_at_least_length(&mut self, expected: usize);
}
impl<'s, T> VecAtLeastLength for Spec<'s, Vec<T>> {
fn has_at_least_length(&mut self, expected: usize) {
let subject = self.subject;
if expected > subject.len() {
AssertionFailure::from_spec(self)
.with_expected(format!("vec with length at least <{}>", expected))
.with_actual(format!("<{}>", subject.len()))
.fail();
}
}
}
依赖项
~465KB