Fix 1.0 发布了。
Fix 是一个 Ruby 的规范框架。它让你用 RFC 2119 的词汇——MUST、SHOULD、MAY——写下一个对象应该做什么,而不涉及它是怎么实现的。规范写好之后,拿任何一个对象去测试它,每条期望得到一个结果。
require "fix"
class Duck
def walk = "Klop klop!"
def swim = "Swoosh..."
end
Fix :Duck do
on :walk do
it MUST eq "Klop klop!"
end
on :swim do
it MUST eq "Swoosh..."
end
on :speak do
it MAY eq "Quaaaaaack!"
end
end
exit(Fix[:Duck].test { Duck.new })
$ ruby duck_fix.rb
duck_fix.rb:10: Success: expected "Klop klop!" to eq "Klop klop!"
duck_fix.rb:14: Success: expected "Swoosh..." to eq "Swoosh..."
duck_fix.rb:18: Info: NoMethodError: undefined method 'speak' for an instance of Duck
$ echo $?
0
Fix :Duck do … end 构建一份规范,并以这个名字注册;Fix[:Duck] 把它找回来。on :walk 向主体发送 walk,里面的 it 判断返回值。it 后面不是块,而是一个要求:一个级别,然后一个匹配器。
鸭子不会说话,但规范只说它 MAY 说话:方法不存在,结果是一条 Info,规范依然满足,进程退出码为 0。换成 MUST,就是一条 Error,退出码为 1。
两个问题把级别区分开来:方法存在吗?MUST 和 SHOULD 要求它存在,MAY 不要求。它做了规范说的事吗?MUST 和 MAY 要求做到,SHOULD 只是建议。于是有五种结果:
| 级别 | 匹配 | 不匹配 | 方法不存在 | 其他异常 |
|---|---|---|---|---|
| MUST | Success | Failure | Error | Error |
| SHOULD | Success | Warning | Error | Error |
| MAY | Success | Failure | Info | Error |
Success、Warning、Info 通过;Failure、Error 不通过。
test 的块构建。同一份规范可以测试 Duck.new,也可以测试任何别的对象。exit)只是它那一条期望的错误。位置: 结果: 摘要,错误的 backtrace 缩进在下面。没有颜色,没有点,没有计数。test 返回是否全部通过,exit 把它变成进程的退出码。lib/ 下 9 个文件,约 300 行代码。Fix 站在四个各自独立的 gem 上:Aw(在子进程里运行一个块,带回结果)、Defi(把一次方法调用当作值)、Matchi(匹配器)、Spectus(RFC 2119 的级别与判定)。规范还可以用 let 和 with 给主体传参数,可以用 fix(:Name) 要求另一份规范。fix-command 提供 fix 命令,把 fix/ 目录下的每个 *_fix.rb 各自放在一个进程里运行。这些都在 README 里。
gem "fix"
需要 Ruby 3.3 或更高版本,以及一个支持 fork 的系统。
这个项目我写了十二年,1.0 是它第一次让我觉得没有什么可以再去掉的。欢迎试用,欢迎反馈。