---
title: 将测试代码翻译为文档
date: '2018-08-02'
slug: test-to-doc
---

我有一个大胆的想法。这个想法萌生于我[召唤壮士帮我写测试](/en/2018/07/add-more-tests/)时。我的 R 包的测试代码都是基于我自己的轻量级 R 包 [testit](https://github.com/yihui/testit)，我不用流行的 testthat 是因为我觉得它把语法搞得太复杂，活生生造出一套词汇。所谓测试，无非就是当（函数）输出与预期不符时，就给个提醒，这个提醒通常是报错。testit 的测试语法很简单，只由一个函数 `assert()` 和一个特殊语法构成，形如

```r
library(testit)
assert('某函数 foo() 应该输出某结果', {
  # 任意 R 代码
  
  (foo() == '预期结果')  # 测试条件
})
```

上面说的特殊语法就是，测试条件要写在一对小括号中，这是为了让 `assert()` 知道花括号内部哪些 R 代码是应该被检测的。每个测试条件必须返回元素全部为 `TRUE` 的逻辑向量（长度大于等于 1）。花括号里可以写任意 R 代码以及多个测试条件，每个条件都得在小括号里。如果测试条件返回的不全是 `TRUE`，那么 `assert()` 就会报错。原理就是这么简单。

一般情况下，只有开发者自己知道测试的存在，用户不会关心一个包里有哪些测试，但其实测试在某种程度上也是宝贵的文档，把测试代码埋在地底下有些可惜。此话怎讲？

文档通常是文字描述。文字描述的问题就是可能会不精确，不同的人理解起来也可能会不一样。测试则是同时给出了源代码和它的预期输出，若读者能同时读这两部分，那么也许可以增强他们对一个函数的理解。代码和数学公式一样，都是精确的表达形式（所以有那句话：“废话少说，放码过来”）。

那么问题来了。测试代码的形式通常比较乱，普通青年应该不会爱读这样的源代码。要是能把代码重新组织一下，做成一篇文章或报告的形式，可读性应该可以大大增加。以 testit 的测试为例：`assert()` 的第一个参数（字符串）可以作为小节标题，花括号内的任意 R 代码可以作为普通代码段，测试条件则可以作为表格或某种双栏形式呈现。比如上面的测试可以翻译为下面的 Markdown 文档：

````md
# 某函数 foo() 应该输出某结果

```r
# 任意 R 代码
```

------- -----------
foo()     预期结果
------- -----------
````

更进一步，如果再用一点[黑魔法](/en/2018/03/hacking-rd/)，把这样的输出塞进相应的 R 帮助文档中，那么一个函数的帮助文档中不仅可以看文字文档、只有源代码的例子，还可以看相关的测试，让用户知道一个函数在不同条件下预期应该返回什么值。那就厉害了我的哥。

上面的从测试到文档的翻译咋整呢？因为 R 继承了 Lisp 的“代码本身就是数据”的特征，你有了代码之后想怎么解析、运行都可以，所以翻译应该不成问题（虽然我也没有十成把握）。我在 `assert()` 中[查探测试条件的小括号](https://github.com/yihui/testit/blob/219f6f45456e/R/testit.R#L104)其实不过就是用了 `match.call()` 和表达式（expression）的简单魔法。

当然，这整件事情还可以完全反过来做，也就是从文档到测试。这个在去年的 R OpenSci 小会上已经[有几位黑客尝试过了](https://github.com/ropenscilabs/testrmd)。
