3个稳定版本
1.0.2 | 2021年2月7日 |
---|---|
1.0.1 | 2020年8月2日 |
#748 在 测试
66 每月下载量
在 dst 中使用
21KB
406 行
fn-fixture
此包提供了一种易于使用的注释,用于测试创建。每个夹具有三个方面
-
一个接受输入并具有实现
std::fmt::Debug
返回类型的函数。 -
一个子文件夹文件夹(可以进一步嵌套,不需要一致),每个以树终止的文件夹都包含一个与函数输入类型相对应的单个输入文件(
.rs
.txt
.bin
)。 -
预期结果,如果不存在,则自动生成。可以将panic视为一个有效的可能预期结果。
此项目遵循惯例而不是配置。输入文件简单地命名: input.rs
input.txt
input.bin
。预期输出文件以夹具名称命名。这意味着多个夹具可以共享测试文件夹树。您不需要自己创建输出文件;如果不存在预期结果,将会自动生成一个。
使用方法
[dev-dependencies]
fn-fixture = "1.0.0"
自给自足
此项目使用自身来测试自身,这使其成为示例和技术解释的三重用途。
snapshot-tests
有四个测试树
-
source
树解释了测试是如何生成的。这是测试fn-fixture
的主要方法。这些测试引用其他两个测试树。 -
code
树提供了当将测试输入到恒等函数时测试会做什么的示例。这是测试fn-fixture
的次要方法。 -
bad
树提供了不正确构建的测试示例,这些测试会导致编译错误。这些测试不是直接由fn-fixture
进行测试的,而是使用预期的编译失败宏在source/bads
中使用。 -
example
树遵循下面的直接示例。
self_snapshots.rs
是使用 fn-fixture
运行这些测试的文件。
示例
此示例是项目的一部分,用于生成和执行。要查看代码生成过程,请参考 snapshot-tests/source/examples
。要查看文件结构,请参考 snapshot-tests/examples
。
制作测试
此测试从文本文件中解析数字
#[fn_fixture::snapshot("snapshot-tests/examples")]
fn parse_unsigned_number(value: &str) -> Result<usize, impl std::fmt::Debug> {
value.parse()
}
#[fn_fixture::snapshot("snapshot-tests/examples")]
fn parse_signed_number(value: &str) -> Result<isize, impl std::fmt::Debug> {
value.parse()
}
快照-测试/示例/错误的数字
:
forty two
快照-测试/示例/正确的数字
:
42
快照-测试/示例/有时是数字
:
-42
初次运行测试
首次运行时,cargo test
将有 6 个测试(2 个固定值,每个 3 个快照)如下所示
test parse_signed_number::sometimes_number ... FAILED
test parse_signed_number::good_number ... FAILED
test parse_unsigned_number::sometimes_number ... FAILED
test parse_unsigned_number::good_number ... FAILED
test parse_unsigned_number::bad_number ... FAILED
test parse_signed_number::bad_number ... FAILED
IntelliJ 会自动为您结构化测试
具体的错误将如下所示
thread 'parse_unsigned_number::bad_number' panicked at 'No expected value set: ...
每个快照文件夹,bad_number
good_number
和 sometimes_number
,现在都将有 parse_signed_number.actual.txt
和 parse_unsigned_number.actual.txt
。
bad_number
的结果将具有
Ok(
Err(
ParseIntError {
kind: InvalidDigit,
},
),
)
good_number
的结果将具有
Ok(
Ok(
42,
),
)
sometimes_number
将分别匹配 parse_unsigned_number
和 parse_signed_number
的结果。
- 注意,最外层的
Ok(
表示线程没有恐慌。如果您期望恐慌,则最外层应为Err(
。
最后,您需要手动审查每个 .actual
文件。如果文件正确,则删除 .actual
。如果不正确,则继续修改代码并运行测试;.actual
将在每个运行中用结果覆盖。
之后
在某些时候,您的输出可能会改变。例如,如果 shapshot-tests/examples/bad_number/parse_unsigned_number.txt
中的一个字符改变(如手动删除期望输出中的 i
),则测试结果将如下所示
---- parse_unsigned_number::bad_number stdout ----
thread 'parse_unsigned_number::bad_number' panicked at 'assertion failed: `(left == right)`
left: `"Ok(\n Err(\n ParseIntError {\n kind: InvalidDigit,\n },\n ),\n)\n"`,
right: `"Ok(\n Err(\n ParseIntError {\n kind: InvaldDigit,\n },\n ),\n)\n"`', fn-fixture\tests\self_snapshots.rs:19:1
内部
此示例的生成代码将如下所示(简化/改写以删除一些边缘情况处理和样板代码)
fn parse_unsigned_number(value: &str, expected_file: &str) {
fn parse_unsigned_number(value: &str) -> Result<usize, impl std::fmt::Debug> {
value.parse()
}
// omitted logic for panic-handling
// omitted logic for writing actual file instead
assert_eq!(
format!("{:#?}\n", parse_unsigned_number(value)),
File::read_to_string(expected_file),
);
}
mod parse_unsigned_number {
#[test] fn bad_number() { super::parse_unsigned_number(include_str!("snapshot-tests/examples/bad_number/input.txt"), "snapshot-tests/examples/bad_number/parse_unsigned_number.txt") }
#[test] fn good_number() { /* ... */ }
#[test] fn sometimes_number() { /* ... */ }
}
fn parse_signed_number(value: &str, expected_file: &str) {
fn parse_signed_number(value: &str) -> Result<isize, impl std::fmt::Debug> {
value.parse()
}
// omitted logic for panic-handling
// omitted logic for writing actual file instead
assert_eq!(
format!("{:#?}\n", parse_signed_number(value)),
File::read_to_string(expected_file),
);
}
mod parse_signed_number {
#[test] fn bad_number() { super::parse_signed_number(include_str!("snapshot-tests/examples/bad_number/input.txt"), "snapshot-tests/examples/bad_number/parse_signed_number.txt") }
#[test] fn good_number() { /* ... */ }
#[test] fn sometimes_number() { /* ... */ }
}
输入类型
input.txt
对应于include_str!(...)
input.bin
对应于include_bytes!(...)
input.rs
对应于include!(...)
- 从
input.rs
获得的类型可以是任何适合于调用 fixture 的单参数的任何类型。snapshot-tests/code
有很多使用 Rust 代码作为输入的例子。
- 从
限制
-
fixture 的名称不能为
input
。那将意味着期望的输出是input.txt
;请将其称为quine
。 -
预期的 panic 应该是
String
或&str
。这很少,如果不是从未出现过问题。每个已知的库都使用这些。例如,panic!("At the disco")
是一个&str
和unwrap()
/expect(...)
使用String
。 -
多行字符串输出应该包裹在
.lines().collect::<Vec<String>>()
中。这些测试是为了让人审查的。IntelliJ 会帮你进行差异比较。 -
每个终止目录(没有子目录的目录)必须恰好有一个
input
文件。 -
有子目录的目录不得有
input
文件。 -
引用的文件夹是顶级文件夹,而不是一个测试本身。
-
在不修改包含文件或清除编译器缓存的情况下添加新测试将被忽略。这是一个编译器级别的限制。
-
树中的每个文件夹都必须是一个有效的 Rust 标识符。这些是嵌套测试模块的命名方式。
-
不建议将其他文件添加到文件夹中,未来版本可能会将它们视为错误。
-
返回类型必须实现
std::fmt::Debug
。 -
不支持注释字段,即使它可调用。
提示
-
将
impl std::fmt::Debug
作为返回类型。 -
为输出的一部分使用不同的函数;不要有一个函数以两种不同的方式解析输入。
-
尽管这个库支持 panic 作为期望的输出,但测试这种行为是不被鼓励的。
-
将您的目录命名为
snapshot-tests
。Snapshot 解释了测试的类型,IDE 可能会识别以-tests
结尾的目录。 -
泛型适用于不同类型的多个
input.rs
文件。
许可证
此应用程序是从内部开发的工具派生出来的,因此根据 MIT 许可证发布
依赖项
~1.5MB
~37K SLoC