# 测试规范

## 1. MoonBit 测试的四种写法

| 名字 | 写法 | 能看到什么 | 用途 |
|---|---|---|---|
| 内联测试 | 写在普通 `.mbt` 里，紧挨着被测函数 | 包内所有成员，包括私有的 | 和被测代码放在同一个文件，改动时不用切换文件 |
| 白盒测试 | 写在 `_wbtest.mbt` 里 | 包内所有成员，包括私有的 | 测内部实现，会用到私有函数或字段 |
| 黑盒测试 | 写在 `_test.mbt` 里 | 只有 `pub` 的公开 API | 站在用户角度测公开 API |
| 文档示例 | 写在公开函数的 `///` 注释里 | 包内所有成员，包括私有的 | 既是文档里的例子，也当测试跑 |

前三种用的都是 `test { }` 语法，区别只在放哪。第四种（文档示例）见第 3 节。

---

## 2. `moon.pkg` 中测试包的依赖配置

`_test.mbt` 和 `_wbtest.mbt` 是两个独立的包，各自有自己的 `import` 段。测试代码用到的依赖要写进对应的段：

```
import {
  "MuVirus/moonfig/core" @config,
} for "test"

import {
  "moonbitlang/core/double",
} for "wbtest"
```

- 写在 `for "test"` 段里的依赖，`_test.mbt` 能用
- 写在 `for "wbtest"` 段里的依赖，`_wbtest.mbt` 能用
- 普通 `.mbt` 文件用到的依赖，写在最上面不带 `for` 的 `import` 块里

写错段会报 `0071` 未导入错误。

---

## 3. 文档注释中的 `mbt check` 示例

文档注释里的代码块可以标记成 `mbt check`，表示这段代码要参与编译和运行。

````moonbit
///|
/// 用一个 `Value` 构造内存来源
///
/// Example:
///
/// ```mbt check
/// test {
///   let cfg = Config::new()
///     .add(MemorySource::new(Value::table({ "debug": Value::bool(false) })))
///     .build()
///   assert_false(cfg.get_bool("debug"))
/// }
/// ```
pub fn MemorySource::new(table : Value) -> MemorySource {
````

写的是 `test { }`，所以它同时满足三件事：

1. **会渲染到生成的文档里** —— 用户在 API 页面上直接看到能跑的用法
2. **会被类型检查** —— API 签名变了，示例立刻报 `4014`
3. **会被真正执行** —— 示例里的断言失效了，`moon test` 立刻报 FAILED

第 2、3 条是普通文字注释做不到的。**写在文档里的文字示例会过期**：改了 API，那段代码还是老的，没人发现。而 `mbt check` 的示例不会过期，它由和真实测试同一套机制检查。

### 3.1 `mbt nocheck`

`mbt nocheck` 是另一种标记，表示这段文档代码**不参与校验**，只作为示例展示。

用在代码在当前包的上下文里编译不过的时候。典型的是带 `import` 的用户侧片段——库自己的包里没有那些依赖：

````moonbit
/// ```mbt nocheck
/// import {
///   "MuVirus/moonfig/core" @config,
/// }
/// ```
````

README 里的片段属于这种情况，所以 README 用 `nocheck`。

---

## 4. 选择规则

```
这段代码在讲什么？
│
├─ 怎么用这个公开 API  ────────────► 文档注释 + ```mbt check
│                                    （既是示例又是测试，还能进文档）
│
├─ 内部逻辑对不对（涉及私有函数）──► _wbtest.mbt
│                                    （成组场景：错误分支、边界、回归）
│
├─ 用户能不能用（只碰 pub API）────► _test.mbt
│                                    （用户视角的完整用法）
│
└─ 给一个小函数补测试 ──────────────► 内联在 .mbt 里
                                     （少见，本项目不用）
```

**公开 API 一律用文档示例，不要用内联测试。** 因为文档示例能做内联测试的全部事，还额外能在文档里被看到。内联测试没有这个优势。

---

## 附录

### 黑盒与白盒

黑盒测试：把被测代码当成一个不透明的盒子，只用它公开的接口去测，不看内部实现。
白盒测试：打开盒子看内部实现，可以直接访问私有函数和私有字段。

MoonBit 把这两种测试编译成两个独立的包：

- `_wbtest.mbt` → 白盒测试包，能看到包内一切，包括私有函数、私有字段
- `_test.mbt` → 黑盒测试包，只能看到 `pub` 的东西

区别的意义在于黑盒测试能发现一类问题：一个只在 `_wbtest.mbt` 里能跑通的测试，一旦你把被测函数从 `pub` 改成 `priv`、或者重命名，黑盒测试不会报错，但用户已经用不了这个 API 了。黑盒测试用的是公开 API，所以能提前发现这种破坏。
