mcpField
M
moonbit-extract-spec-test

moonbit-extract-spec-test

github.com/moonbitlang测试与质量34256 次安装6 周前更新
代码仓库

关于 moonbit-extract-spec-test

moonbit-extract-spec-test 是面向 MoonBit 开发者的技能,用于从已有实现提取 API 规格并生成规格驱动测试。它涵盖有效、无效输入及边界情况,并指导验证规格和测试。

主要功能

  1. 01梳理公开 API 与错误类型
  2. 02生成声明式规格文件
  3. 03按有效与无效用例组织测试
  4. 04覆盖边界、回归和集成场景
  5. 05运行类型检查与测试验证

适用场景

  1. 01为已有 MoonBit 包提取 API 契约
  2. 02为现有实现补充规格驱动测试
  3. 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 函数都有测试
  • 检查是否存在未测试的错误路径
  • 确认已覆盖边界情况

核心原则

规范文件规则
  1. 仅声明桩:所有公共 API 都使用 declare 关键字(无需函数体)
  2. 精确签名:与实现的类型签名完全一致
  3. 错误处理:包含所有错误类型,并添加适当的派生项
  4. 文档:保留或添加文档注释,以提高清晰度
  5. 测试辅助函数:包含 to_test_json() 等转换函数,便于检查
测试组织
  1. 黑盒测试:测试仅使用公共 API(通过 @pkg.function 调用)
  2. 快照测试:复杂值优先使用 json_inspect()
  3. 清晰命名:使用具有描述性的测试名称,例如“valid/arrays/nested”或“invalid/keys/empty”
  4. 分类整理:按有效性组织(正常路径放在 <pkg>_valid_test.mbt,错误路径放在 <pkg>_invalid_test.mbt)
  5. 代码块分隔:使用 ///| 分隔测试代码块
测试覆盖目标
  • 正向用例:产生预期输出的有效输入
  • 负向用例:产生适当错误的无效输入
  • 边界情况:边界、空输入、特殊值
  • 集成:各项功能协同工作
  • 目标是达到 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