7 个版本 (破坏性更新)
0.7.0 | 2024 年 7 月 1 日 |
---|---|
0.6.0 | 2024 年 6 月 13 日 |
0.5.0 | 2024 年 5 月 31 日 |
0.4.0 | 2024 年 5 月 29 日 |
0.1.0 | 2024 年 5 月 1 日 |
#135 在 测试
每月 128 次下载
61KB
712 行
Goldenscript
一个基于 Cockroach Labs 的 Go 语言 datadriven
框架的 Rust 测试框架。它结合了多种测试技术,使得编写和更新测试用例变得简单高效
Goldenscript 是一个包含一组任意输入命令及其预期文本输出的纯文本文件,由 ---
分隔
command
---
output
command argument
command key=value
---
output
命令由提供的 Runner
执行。预期的输出通常不是手工编写,而是通过运行具有环境变量 UPDATE_GOLDENFILES=1
的测试来生成的
$ UPDATE_GOLDENFILES=1 cargo test
然后通过检查和提交到版本控制来验证这些文件。如果测试与预期输出不匹配,则测试将失败并显示差异
这种方法特别适用于测试复杂的状态系统,例如数据库操作、网络协议或语言解析。手工编写和断言此类案例可能既繁琐又费时,因此脚本化和记录这些交互通常可以在成本的一小部分内提供更好的测试覆盖率。
内部,使用 goldenfile
包来管理黄金文件。
文档
请参阅 包文档,其中包含有关语法和功能的更多信息。
示例
有关现实世界的示例,请参见等。
- toyDB Raft:分布式一致性集群。
- toyDB MVCC:ACID事务。
- goldenscript 解析器:Goldenscript 使用自身来测试其解析器和运行器。
以下是一个基本示例,测试 Rust 标准库的 BTreeMap
。
# Tests the Rust standard library BTreeMap.
# Get and range returns nothing for an empty map.
get foo
range
---
get → None
# Inserting keys out of order will return them in order. Silence the insert
# output with ().
(insert b=2 a=1 c=3)
range
---
a=1
b=2
c=3
# Getting a key returns its value.
get b
---
get → Some("2")
# Bounded scans, where the end is exclusive.
range b
---
b=2
c=3
range a c
---
a=1
b=2
# An end bound less than the start bound panics. Expect the failure with !.
!range b a
---
Panic: range start is greater than range end in BTreeMap
# Replacing a key updates the value and returns the old one.
insert b=foo
get b
---
insert → Some("2")
get → Some("foo")
此脚本的对应运行器
#[derive(Default)]
struct BTreeMapRunner {
map: std::collections::BTreeMap<String, String>,
}
impl goldenscript::Runner for BTreeMapRunner {
fn run(&mut self, command: &goldenscript::Command) -> Result<String, Box<dyn Error>> {
let mut output = String::new();
match command.name.as_str() {
// get KEY: fetches the value of the given key, or None if it does not exist.
"get" => {
let mut args = command.consume_args();
let key = &args.next_pos().ok_or("key not given")?.value;
args.reject_rest()?;
let value = self.map.get(key);
writeln!(output, "get → {value:?}")?;
}
// insert KEY=VALUE...: inserts the given key/value pairs, returning the old value.
"insert" => {
let mut args = command.consume_args();
for arg in args.rest_key() {
let old = self.map.insert(arg.key.clone().unwrap(), arg.value.clone());
writeln!(output, "insert → {old:?}")?;
}
args.reject_rest()?;
}
// range [FROM] [TO]: iterates over the key/value pairs in the range from..to.
"range" => {
use std::ops::Bound::*;
let mut args = command.consume_args();
let from = args.next_pos().map(|a| Included(a.value.clone())).unwrap_or(Unbounded);
let to = args.next_pos().map(|a| Excluded(a.value.clone())).unwrap_or(Unbounded);
args.reject_rest()?;
for (key, value) in self.map.range((from, to)) {
writeln!(output, "{key}={value}")?;
}
}
name => return Err(format!("invalid command {name}").into()),
};
Ok(output)
}
}
#[test]
fn btreemap() {
goldenscript::run(&mut BTreeMapRunner::default(), "btreemap").expect("goldenscript failed")
}
依赖项
约3-11MB
约135K SLoC