moonbit-extract-spec-test

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MoonBit Extract Spec & Test

MoonBit 提取规范与测试

Overview

概述

Reverse-engineer a formal API contract (
<pkg>_spec.mbt
) and comprehensive test suites from an existing MoonBit implementation. This enables spec-driven testing for already-written code.
从现有的MoonBit实现反向工程生成正式的API契约(
<pkg>_spec.mbt
)和全面的测试套件。这能为已编写的代码实现规范驱动测试。

When 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
    pub
    and
    pub(all)
    types, functions, traits
  • 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
    pub(all)
    类型、函数、trait
  • 记录错误类型(子错误)及其变体
  • 记录方法签名和trait实现
  • 识别关键入口点和核心功能
检查测试模式(如果存在测试):
  • 查找现有的测试文件(
    *_test.mbt
    *_wbtest.mbt
  • 识别测试覆盖缺口
  • 记录常见测试场景

2. Generate the Spec File (
<pkg>_spec.mbt
)

2. 生成规范文件(
<pkg>_spec.mbt

Create formal API contract:
  • Use the
    declare
    keyword for all public functions/types
  • Preserve exact type signatures from implementation
  • Include error types with
    derive(Show, Eq, ToJson)
    for testability
  • Add documentation comments from original code
  • Declarations with
    declare
    do not have a body
  • Use
    pub(all)
    for types that need construction in tests
  • Keep
    pub
    for opaque types
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 check
Run the tests:
bash
moon test          # Run all tests
moon test -u       # Update snapshot tests
Verify 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

规范文件规则

  1. Declaration-only stubs: All public API uses
    declare
    keyword (no body needed)
  2. Exact signatures: Match the implementation's type signatures precisely
  3. Error handling: Include all error types with proper derives
  4. Documentation: Preserve or add doc comments for clarity
  5. Test helpers: Include conversion functions like
    to_test_json()
    for inspection
  1. 仅声明存根:所有公共API使用
    declare
    关键字(无需函数体)
  2. 精确签名:与实现中的类型签名完全匹配
  3. 错误处理:包含所有带有正确derive的错误类型
  4. 文档:保留或添加文档注释以提升清晰度
  5. 测试辅助函数:包含
    to_test_json()
    等转换函数用于检查

Test Organization

测试组织规则

  1. Black-box testing: Tests use only public API (call via
    @pkg.function
    )
  2. Snapshot testing: Prefer
    json_inspect()
    for complex values
  3. Clear naming: Use descriptive test names like "valid/arrays/nested" or "invalid/keys/empty"
  4. Categorization: Organize by validity (
    <pkg>_valid_test.mbt
    for happy path,
    <pkg>_invalid_test.mbt
    for error path)
  5. Block separation: Use
    ///|
    to delimit test blocks
  1. 黑盒测试:测试仅使用公共API(通过
    @pkg.function
    调用)
  2. 快照测试:对复杂值优先使用
    json_inspect()
  3. 清晰命名:使用描述性测试名称,如"valid/arrays/nested"或"invalid/keys/empty"
  4. 分类管理:按有效性组织(
    <pkg>_valid_test.mbt
    用于正常路径,
    <pkg>_invalid_test.mbt
    用于错误路径)
  5. 块分隔:使用
    ///|
    分隔测试块

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
    moon check
    frequently to catch type errors early
  • Update snapshots: Use
    moon test -u
    when output format changes
  • 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" }
})