moonbit-extract-spec-test
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMoonBit Extract Spec & Test
MoonBit 提取规范与测试
Overview
概述
Reverse-engineer a formal API contract () and comprehensive test suites from an existing MoonBit implementation. This enables spec-driven testing for already-written code.
<pkg>_spec.mbt从现有的MoonBit实现反向工程生成正式的API契约()和全面的测试套件。这能为已编写的代码实现规范驱动测试。
<pkg>_spec.mbtWhen to Use
使用场景
- User asks to "extract spec from existing implementation"
- User wants to "generate spec-driven tests for a package"
- User requests "create formal API specification from code"
- Converting legacy code to spec-driven development workflow
- Need to document and test existing public API surface
- 用户要求“从现有实现中提取规范”
- 用户希望“为包生成规范驱动的测试”
- 用户请求“从代码创建正式的API规范”
- 将遗留代码转换为规范驱动的开发工作流
- 需要记录并测试现有的公共API接口
Workflow
工作流程
1. Analyze the Existing Implementation
1. 分析现有实现
Identify public API surface:
- List all and
pubtypes, functions, traitspub(all) - Note error types (suberrors) and their variants
- Document method signatures and trait implementations
- Identify key entry points and core functionality
Examine test patterns (if tests exist):
- Look for existing test files (,
*_test.mbt)*_wbtest.mbt - Identify test coverage gaps
- Note common test scenarios
识别公共API表面:
- 列出所有和
pub类型、函数、traitpub(all) - 记录错误类型(子错误)及其变体
- 记录方法签名和trait实现
- 识别关键入口点和核心功能
检查测试模式(如果存在测试):
- 查找现有的测试文件(、
*_test.mbt)*_wbtest.mbt - 识别测试覆盖缺口
- 记录常见测试场景
2. Generate the Spec File (<pkg>_spec.mbt
)
<pkg>_spec.mbt2. 生成规范文件(<pkg>_spec.mbt
)
<pkg>_spec.mbtCreate formal API contract:
- Use the keyword for all public functions/types
declare - Preserve exact type signatures from implementation
- Include error types with for testability
derive(Show, Eq, ToJson) - Add documentation comments from original code
- Declarations with do not have a body
declare - Use for types that need construction in tests
pub(all) - Keep for opaque types
pub
Example spec structure:
mbt
///|
/// 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]创建正式API契约:
- 对所有公共函数/类型使用关键字
declare - 保留实现中的精确类型签名
- 包含带有的错误类型以支持可测试性
derive(Show, Eq, ToJson) - 添加原始代码中的文档注释
- 使用的声明不需要函数体
declare - 对测试中需要构造的类型使用
pub(all) - 对不透明类型使用
pub
规范结构示例:
mbt
///|
/// 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. Extract and Organize Test Cases
3. 提取并整理测试用例
Categorize tests by validity (happy path vs error path):
Valid tests ():
<pkg>_valid_test.mbt- Happy path: valid inputs that exercise core features
- Edge cases: boundary sizes, empty inputs, maximum nesting
- Compatibility: behaviors that match real-world data or dominant implementations
- Regression: bugs fixed in prior work or known pitfalls
Invalid tests ():
<pkg>_invalid_test.mbt- Error path: invalid inputs and required error behavior
- Ambiguities: inputs where the spec allows multiple interpretations (tests pin down chosen interpretation)
- Malformed inputs: syntax errors, type mismatches, constraint violations
按有效性分类测试(正常路径 vs 错误路径):
有效测试():
<pkg>_valid_test.mbt- 正常路径:能触发核心功能的有效输入
- 边缘情况:边界大小、空输入、最大嵌套层级
- 兼容性:与真实数据或主流实现匹配的行为
- 回归测试:之前修复的bug或已知陷阱
无效测试():
<pkg>_invalid_test.mbt- 错误路径:无效输入及预期的错误行为
- 歧义情况:规范允许多种解释的输入(测试明确选定的解释)
- 格式错误的输入:语法错误、类型不匹配、约束违反
4. Write Spec-Driven Tests
4. 编写规范驱动的测试
For valid input tests:
mbt
///|
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" }
})
}For invalid input tests:
mbt
///|
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(_) => ()
}
}有效输入测试示例:
mbt
///|
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" }
})
}无效输入测试示例:
mbt
///|
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. Validate
5. 验证
Type-check the spec:
bash
moon checkRun the tests:
bash
moon test # Run all tests
moon test -u # Update snapshot testsVerify coverage:
- Ensure all public API functions have tests
- Check for untested error paths
- Confirm edge cases are covered
对规范进行类型检查:
bash
moon check运行测试:
bash
moon test # 运行所有测试
moon test -u # 更新快照测试验证覆盖范围:
- 确保所有公共API函数都有测试覆盖
- 检查未测试的错误路径
- 确认边缘情况已覆盖
Key Principles
核心原则
Spec File Rules
规范文件规则
- Declaration-only stubs: All public API uses keyword (no body needed)
declare - Exact signatures: Match the implementation's type signatures precisely
- Error handling: Include all error types with proper derives
- Documentation: Preserve or add doc comments for clarity
- Test helpers: Include conversion functions like for inspection
to_test_json()
- 仅声明存根:所有公共API使用关键字(无需函数体)
declare - 精确签名:与实现中的类型签名完全匹配
- 错误处理:包含所有带有正确derive的错误类型
- 文档:保留或添加文档注释以提升清晰度
- 测试辅助函数:包含等转换函数用于检查
to_test_json()
Test Organization
测试组织规则
- Black-box testing: Tests use only public API (call via )
@pkg.function - Snapshot testing: Prefer for complex values
json_inspect() - Clear naming: Use descriptive test names like "valid/arrays/nested" or "invalid/keys/empty"
- Categorization: Organize by validity (for happy path,
<pkg>_valid_test.mbtfor error path)<pkg>_invalid_test.mbt - Block separation: Use to delimit test blocks
///|
- 黑盒测试:测试仅使用公共API(通过调用)
@pkg.function - 快照测试:对复杂值优先使用
json_inspect() - 清晰命名:使用描述性测试名称,如"valid/arrays/nested"或"invalid/keys/empty"
- 分类管理:按有效性组织(用于正常路径,
<pkg>_valid_test.mbt用于错误路径)<pkg>_invalid_test.mbt - 块分隔:使用分隔测试块
///|
Test Coverage Goals
测试覆盖目标
- Positive cases: Valid inputs producing expected outputs
- Negative cases: Invalid inputs producing appropriate errors
- Edge cases: Boundaries, empty inputs, special values
- Integration: Features working together
- Aim for 80-90% API coverage
- 正向用例:有效输入产生预期输出
- 负向用例:无效输入产生相应错误
- 边缘用例:边界值、空输入、特殊值
- 集成测试:功能协同工作的场景
- 目标:80-90%的API覆盖率
Example Extraction
提取示例
From Implementation
从实现代码
mbt
pub type Config
pub fn Config::parse(text : String) -> Result[Config, String] {
// implementation
}
pub fn Config::get_value(self : Config, key : String) -> String? {
// implementation
}mbt
pub type Config
pub fn Config::parse(text : String) -> Result[Config, String] {
// implementation
}
pub fn Config::get_value(self : Config, key : String) -> String? {
// implementation
}To Spec
生成规范
mbt
///|
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?mbt
///|
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?To Tests
生成测试
mbt
///|
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(_) => ()
}
}mbt
///|
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(_) => ()
}
}Tips
小贴士
- Start simple: Begin with core functionality, add complexity incrementally
- Use existing tests: If tests exist, migrate and enhance them
- Iterate: Run frequently to catch type errors early
moon check - Update snapshots: Use when output format changes
moon test -u - Document reasoning: Add comments explaining non-obvious test cases
- 从简入手:先从核心功能开始,逐步增加复杂度
- 利用现有测试:如果已有测试,迁移并增强它们
- 迭代开发:频繁运行以尽早发现类型错误
moon check - 更新快照:当输出格式变化时使用
moon test -u - 记录思路:为非显而易见的测试用例添加注释说明
Common Patterns
常见模式
Testing with Result types
测试Result类型
mbt
let result : Result[T, Error] = try? function_call(...)
match result {
Ok(value) => inspect(value, content="...")
Err(e) => fail("Unexpected error: \{e}")
}mbt
let result : Result[T, Error] = try? function_call(...)
match result {
Ok(value) => inspect(value, content="...")
Err(e) => fail("Unexpected error: \{e}")
}Testing expected failures
测试预期失败场景
mbt
match try? function_call(...) {
Ok(v) => fail("Expected error, got \{v}")
Err(_) => () // Success - error was expected
}mbt
match try? function_call(...) {
Ok(v) => fail("Expected error, got \{v}")
Err(_) => () // Success - error was expected
}Using test JSON format
使用测试JSON格式
mbt
json_inspect(value.to_test_json(), content={
"field": { "type": "integer", "value": "42" }
})mbt
json_inspect(value.to_test_json(), content={
"field": { "type": "integer", "value": "42" }
})