8 个版本 (5 个破坏性更新)
使用旧的 Rust 2015
0.6.0 | 2016 年 12 月 10 日 |
---|---|
0.5.2 | 2016 年 11 月 13 日 |
0.5.0 | 2016 年 10 月 30 日 |
0.4.0 | 2016 年 10 月 14 日 |
0.1.0 | 2016 年 9 月 12 日 |
#2457 in Rust 模式
9,349 每月下载量
在 少于 66 crates 中使用
125KB
2K SLoC
spectral
Rust 的流畅测试断言。
受 Google Truth 和其他流畅断言框架的影响。
用法
将以下内容添加到您的 Cargo.toml
[dependencies]
spectral = "0.6.0"
然后将其添加到您的 crate 中
extern crate spectral
要快速开始使用断言,只需在测试模块中使用 prelude
模块即可
use spectral::prelude::*;
概述
Spectral 允许您通过分离您要测试的内容、测试对象以及断言方式来以流畅的方式编写断言。
简单的断言
例如,为了测试产生的值是否等于预期值,您可以编写
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
命名主题
为了让您的主题更加明确,您可以在.named(...)
之后调用assert_that
(或asserting(...).that(...)
),如果断言失败,它将打印提供的&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)
此外,这将为您提供失败断言的文件和行号(而不仅仅是内部光谱恐慌位置)。
断言(基本)
注意:每个断言的描述和示例在本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
向量
has_length
is_empty
HashMaps
has_length
is_empty
contains_key -> (返回一个包含键值的新Spec实例)
does_not_contain_key
contains_entry
does_not_contain_entry
IntoIterator/Iterator
contains
does_not_contain
contains_all_of
mapped_contains
equals_iterator
IntoIterator
matching_contains
可选功能
Num Crate
使用num
crate进行浮点数断言。此功能默认启用,但如果您不想依赖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> to not equal <1>
but was: equal
matches
接受一个接受主题类型并返回bool值的函数。返回false将导致断言失败。
注意:生成的恐慌信息将仅显示实际值。建议您编写自己的断言,而不是依赖于这个。
示例
assert_that(&"Hello").matches(|val| val.eq(&"Hello"));
失败消息
expectation failed for value <"Hello">
布尔值
is_true
断言主题为真。主题类型必须是 bool
。
示例
assert_that(&true).is_true();
失败消息
expected: bool to be <true>
but was: <false>
is_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");
失败消息
expected: string containing <"A">
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
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。
要使断言失败,在你的断言方法中创建一个新的 AssertionFailure
结构体,使用 from_spec(...)
,并在其中传入 self
。
AssertionFailure
还实现了构建器方法 with_expected(...)
、with_actual(...)
和 fail(...)
,这提供了失败测试的常规消息格式的必要功能。如果你需要更大的控制失败消息,你可以调用 fail_with_message(...)
,它将直接打印提供的消息。
在两种情况下,使用 asserting(...)
提供的任何描述都将始终附加到 panic 消息之前。
例如,要创建一个断言,断言 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();
}
}
}
依赖项
~240KB