mcpField
T
testing

testing

github.com/tursodatabase测试与质量25.1k1.3k 次安装今天更新
代码仓库

关于 testing

这份 Turso 测试指南面向项目贡献者,介绍测试类型的选择、编写方法和运行方式。它说明了优先采用的 `.sqltest`,以及逐步淘汰中的 TCL 测试、Rust 集成测试和模糊测试。

主要功能

  1. 01测试类型选择指南
  2. 02`.sqltest` 编写与转换
  3. 03TCL 与 Rust 集成测试
  4. 04用于发现边界情况的模糊测试
  5. 05断言方法与测试运行命令

适用场景

  1. 01补充 SQL 兼容性测试
  2. 02为复杂行为编写回归测试
  3. 03通过模糊测试发现边界情况

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tursodatabase/turso --skill testing

安装后在 Claude Code 中输入/testing确认已加载。

详细介绍

测试指南

测试类型及适用场景

类型位置适用场景
.sqltestsqlite/conformance/sqlite-sqltests/SQL 兼容性。新测试优先使用
TCL .testtesting/旧版 SQL 兼容性测试(正在逐步淘汰)
Rust 集成测试tests/integration/回归测试、复杂场景
模糊测试tests/fuzz/复杂功能、发现边界情况

注意: TCL 测试正在逐步淘汰,改用 sqlite/conformance/ 中的 .sqltest 测试套件。.sqltest 格式可以让同一组测试用例在多个后端(CLI、Rust 绑定等)上运行。

运行测试

# Main test suite (TCL compat, sqlite3 compat, Python wrappers)
make test

# Single TCL test
make test-single TEST=select.test

# SQL test runner
make -C sqlite/conformance run-cli

# OR
cargo run -p sqltest -- run <test-file or directory>

# Rust unit/integration tests (full workspace)
cargo test

编写测试

.sqltest(优先使用)
@database :default:

test example-addition {
    SELECT 1 + 1;
}
expect {
    2
}

test example-multiple-rows {
    SELECT id, name FROM users WHERE id < 3;
}
expect {
    1|alice
    2|bob
}

位置:sqlite/conformance/sqlite-sqltests/*.sqltest

必须先使用测试运行器的 convert 命令开始转换 TCL 测试(例如 cargo run -- convert <TCL_test_path> -o <out_dir>)。转换结果并不总是准确,但能处理大多数测试。如果转换时出现警告,你需要手动补全缺失内容(例如手动展开 for each 循环)。然后,运行 make -C sqlite/conformance run-rust 验证测试是否有效;如果转换结果有误,则调整测试输出。此外,TCL 测试使用硬编码数据库,而 .sqltest 会使用不同的种子生成数据库,因此你可能需要修改预期测试结果,使之与新的数据库查询输出一致。避免修改测试中的 SQL 语句,只修改预期结果。

TCL
do_execsql_test_on_specific_db {:memory:} test-name {
  SELECT 1 + 1;
} {2}

位置:testing/*.test

Rust 集成测试
// tests/integration/test_foo.rs
#[test]
fn test_something() {
    let conn = Connection::open_in_memory().unwrap();
    // ...
}
断言

除了最简单的断言外,优先使用 asserting crate。它提供了丰富的断言,能让测试更易读。以下是一些简单示例:

use asserting::prelude::*;

assert_that!(conn.execute("SELECT * FROM t1;")).is_err();
assert_that_code!(|| parse(bad_input)).panics_with_message("unexpected token");

assert_that!(&rows)
    .has_length(3)
    .any_satisfies(|r| r
    .name == "bob")
    .first_element_ref()
    .is_equal_to(&Row { id: 1, name: "alice".into() });

assert_that!(&header)
    .named("page 2 header")
    .satisfies_with_message("be a leaf table page", |h| h[0] == 0x0d);

// soft assertions: mark the test as failed but don't stop
verify_that!(&plan)
    .starts_with("SEARCH")
    .contains("USING INDEX")
    .soft_panic();

crate::assertions 为结果行提供了 row!,还提供了 column 和查询计划断言:

use crate::assertions::{AssertColumn, AssertQueryPlan, Cell, NULL};

assert_that!(limbo_exec_rows(&conn, "SELECT id, name FROM t ORDER BY id"))
    .is_equal_to(vec![row![1, "alice"], row![2, NULL]]);

assert_that!(limbo_exec_rows(&conn, "SELECT id FROM t WHERE id = 1"))
    .single_element()
    .is_equal_to(row![1]);

assert_that!(limbo_exec_rows(&conn, "SELECT id, name FROM t"))
    .column(1)
    .contains(Cell::from("alice"));

assert_that!(limbo_exec_rows(&conn, "EXPLAIN QUERY PLAN SELECT id FROM t WHERE name = 'a'"))
    .uses_index("idx_name")
    .searches_table("t")
    .has_table_access_order(["t"]);

重要规则

  • 每项功能变更都需要测试
  • 测试必须在没有变更时失败,在有变更时通过
  • 优先使用内存数据库::memory:(sqltest)或 {:memory:}(TCL)
  • 不要自创测试格式。遵循现有模式
  • 尽可能先编写测试

测试数据库架构

testing/system/testing.db 包含 users 和 products 表。架构详见 docs/testing.md。

测试期间的日志记录

RUST_LOG=none,turso_core=trace make test

输出位置:testing/system/test.log。警告:日志非常详细。

评论

支持 Markdown