关于 moonbit-extract-spec-test
moonbit-extract-spec-test 是面向 MoonBit 开发者的技能,用于从已有实现提取 API 规格并生成规格驱动测试。它涵盖有效、无效输入及边界情况,并指导验证规格和测试。
主要功能
- 01梳理公开 API 与错误类型
- 02生成声明式规格文件
- 03按有效与无效用例组织测试
- 04覆盖边界、回归和集成场景
- 05运行类型检查与测试验证
适用场景
- 01为已有 MoonBit 包提取 API 契约
- 02为现有实现补充规格驱动测试
- 03将遗留代码纳入规格驱动开发
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ moonbitlang/ skills --skill moonbit-extract-spec-test安装后在 Claude Code 中输入/moonbit-extract-spec-test确认已加载。
详细介绍
MoonBit 提取规范与测试
概述
从现有的 MoonBit 实现中逆向工程出正式的 API 契约(<pkg>_spec.mbt)和全面的测试套件。这使得已编写的代码也能进行规范驱动测试。
适用场景
- 用户要求“从现有实现中提取规范”
- 用户希望“为某个包生成规范驱动测试”
- 用户请求“根据代码创建正式的 API 规范”
- 将遗留代码转换为规范驱动的开发工作流
- 需要记录并测试现有的公共 API
工作流
1. 分析现有实现
识别公共 API:
- 列出所有
pub和pub(all)类型、函数及 trait - 记录错误类型(子错误)及其变体
- 记录方法签名和 trait 实现
- 找出关键入口点和核心功能
检查测试模式(如果存在测试):
- 查找现有测试文件(
*_test.mbt、*_wbtest.mbt) - 找出测试覆盖方面的空白
- 记录常见测试场景
2. 生成规范文件(<pkg>_spec.mbt)
创建正式的 API 契约:
- 对所有公共函数和类型使用
declare关键字 - 保留实现中的精确类型签名
- 包含带有
derive(Show, Eq, ToJson)的错误类型,以便测试 - 添加原始代码中的文档注释
- 使用
declare声明的内容不需要函数体 - 对需要在测试中构造的类型使用
pub(all) - 不透明类型保留
pub
规范结构示例:
///|
/// Error type for parsing operations
pub(all) suberror ParseError {
InvalidFormat(String)
UnexpectedEof
} derive(Show, Eq, ToJson)
///|
/// Main data type
declare pub(all) type Toml
///|
/// Convert to test JSON format
declare pub fn Toml::to_test_json(self : Toml) -> Json
///|
/// Parse TOML from string
declare pub fn parse(input : StringView) -> Result[Toml, ParseError]
3. 提取并整理测试用例
按有效性分类测试(正常路径与错误路径):
有效测试(<pkg>_valid_test.mbt):
- 正常路径:用于验证核心功能的有效输入
- 边界情况:边界大小、空输入、最大嵌套层级
- 兼容性:与真实数据或主流实现相符的行为
- 回归:先前工作中修复的错误或已知陷阱
无效测试(<pkg>_invalid_test.mbt):
- 错误路径:无效输入及要求的错误行为
- 模糊情况:规范允许多种解释的输入(测试明确选定的解释)
- 格式错误的输入:语法错误、类型不匹配、违反约束
4. 编写规范驱动测试
有效输入测试:
///|
test "valid/category/test-name" {
let toml_text =
#|key = "value"
#|
let toml = match @pkg.parse(toml_text) {
Ok(toml) => toml
Err(error) => fail("Failed to parse: " + error.to_string())
}
json_inspect(toml.to_test_json(), content={
"key": { "type": "string", "value": "value" }
})
}
无效输入测试:
///|
test "invalid/category/test-name" {
let toml_text =
#|invalid syntax here
#|
match @pkg.parse(toml_text) {
Ok(toml) => {
let json = toml.to_test_json()
fail("Expected parse to fail, got \{json.stringify(indent=2)}")
}
Err(_) => ()
}
}
5. 验证
对规范进行类型检查:
moon check
运行测试:
moon test # Run all tests
moon test -u # Update snapshot tests
检查覆盖率:
- 确保所有公共 API 函数都有测试
- 检查是否存在未测试的错误路径
- 确认已覆盖边界情况
核心原则
规范文件规则
- 仅声明桩:所有公共 API 都使用
declare关键字(无需函数体) - 精确签名:与实现的类型签名完全一致
- 错误处理:包含所有错误类型,并添加适当的派生项
- 文档:保留或添加文档注释,以提高清晰度
- 测试辅助函数:包含
to_test_json()等转换函数,便于检查
测试组织
- 黑盒测试:测试仅使用公共 API(通过
@pkg.function调用) - 快照测试:复杂值优先使用
json_inspect() - 清晰命名:使用具有描述性的测试名称,例如“valid/arrays/nested”或“invalid/keys/empty”
- 分类整理:按有效性组织(正常路径放在
<pkg>_valid_test.mbt,错误路径放在<pkg>_invalid_test.mbt) - 代码块分隔:使用
///|分隔测试代码块
测试覆盖目标
- 正向用例:产生预期输出的有效输入
- 负向用例:产生适当错误的无效输入
- 边界情况:边界、空输入、特殊值
- 集成:各项功能协同工作
- 目标是达到 80-90% 的 API 覆盖率
提取示例
从实现到规范
pub type Config
pub fn Config::parse(text : String) -> Result[Config, String] {
// implementation
}
pub fn Config::get_value(self : Config, key : String) -> String? {
// implementation
}
生成规范
///|
declare pub type Config
///|
declare pub fn Config::parse(text : String) -> Result[Config, String]
///|
declare pub fn Config::get_value(self : Config, key : String) -> String?
编写测试
///|
test "parse valid config" {
let result = try? Config::parse("key=value")
match result {
Ok(cfg) => {
inspect(cfg.get_value("key"), content="Some(\"value\")")
}
Err(e) => fail("Parse failed: \{e}")
}
}
///|
test "parse invalid config returns error" {
let result = try? Config::parse("invalid")
match result {
Ok(_) => fail("Should have failed to parse")
Err(_) => ()
}
}
提示
- 从简单开始:先实现核心功能,再逐步增加复杂度
- 使用现有测试:如果已有测试,请迁移并加以完善
- 迭代改进:经常运行
moon check,及早发现类型错误 - 更新快照:输出格式发生变化时,使用
moon test -u - 说明理由:添加注释,解释不明显的测试用例
常见模式
使用 Result 类型进行测试
let result : Result[T, Error] = try? function_call(...)
match result {
Ok(value) => inspect(value, content="...")
Err(e) => fail("Unexpected error: \{e}")
}
测试预期失败
match try? function_call(...) {
Ok(v) => fail("Expected error, got \{v}")
Err(_) => () // Success - error was expected
}
使用测试 JSON 格式
json_inspect(value.to_test_json(), content={
"field": { "type": "integer", "value": "42" }
})
评论
支持 Markdown