OpenResty单元测试实践

无测试,不编码。有持续运行的单元测试,是保持项目健康最基本的要求。在多人协做的内部项目中,这一点尤为重要。
基于 OpenResty 的项目开发天然不会例外。html

测试框架

咱们考察过 OpenResty 本身的测试框架 test-nginx,发现该框架偏向于对接口进行测试。把它用做单元测试,犹如用园艺剪裁纸。
以 lua 应用的角度看,busted却是个合适的单元测试框架。只是 OpenResty 项目代码中不免会用到 OpenResty 的 API。
若是不能在 OpenResty 上下文中运行测试,那么这些 API 就没法调用。显然咱们不可能把这些 API 都分离出去,或者 mock 掉。
这么作不切实际。nginx

好在 OpenResty 提供了 resty 命令行工具,可以以一次性命令的形式在 OpenResty 上下文运行给定的 lua 代码。
结合 restybusted 两个工具,有一个办法能够在 OpenResty 上下文中运行 busted 的测试代码。git

首先,注意要安装 lua5.1 对应的 luarocks。luarocks 默认的 lua 版本是 5.2, 因此安装的时候须要配置一下。
若是可以像这样,直接指定使用安装 OpenResty 时附带的 luajit,那就更好了:程序员

./configure --with-lua="/usr/local/openresty/luajit" --lua-suffix="jit" --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1/

而后 sudo luarocks install busted, 安装 busted。github

查看命令行工具 busted 的内容,会发现它实际上是个启动脚本:框架

#!/usr/bin/env lua
-- Busted command-line runner
require 'busted.runner'({ standalone = false })

OK,如今就让 resty 去执行这个脚本吧!函数

假设项目结构以下:工具

..
├── src
│   └── code.lua
└── test
    └── test_spec.lua
    └── busted_runner.lua

其中 busted_runner.lua 的内容是:单元测试

require 'busted.runner'({ standalone = false })

咱们能够这样运行:测试

# 当前工做目录是 test
resty -I ../src busted_runner.lua --verbose test_spec.lua

解释下,-I 参数后面跟着的是 lua 模块的路径,后面跟着的是要执行的代码文件和用户参数。因此上面的命令等价于 busted --verbose test_spec.lua

使用 resty 来运行 lua 代码有一个局限。它会在 ngx.timer 的上下文运行代码,致使许多跟请求上下文相关的 OpenResty API
依然是没法调用的。举个例子,若是待测试代码里面调用了 ngx.location.capture,执行测试会获得这样的结果:

Error → test_spec.lua @ 7
topic feature
test_spec.lua:15: API disabled in the current context

不过这已足矣,毕竟咱们是在作单元测试而非接口测试。像是请求上下文的东西,就应该在测试时 mock 掉。至于怎么 mock,这属于
busted 的范畴,请参考 busted 的文档。

测试覆盖

有了测试,下面的需求就是统计测试的覆盖。

倘若有具体的测试覆盖程度,程序员们能够针对性地编写测试,对哪些地方缺少测试也心知肚明。
另外,还能够求出项目的测试覆盖率。
测试覆盖率不单单可以衡量项目的健康程度。覆盖率的增增减减,会激励程序员们尽量地多写测试。
谁会愿意看着本身新增的代码一片红色(无测试覆盖)?天然而然的,若是每次提交的时候,能够及时得到测试覆盖的反馈,便能保持一个较高的覆盖率。

在 lua 中有一个库 luacov ,能够实现这样的功能。
在测试运行时加入 luacov ,它会在每一行加入钩子函数,触发对测试覆盖的统计。luacov 会把统计到的覆盖率报告在
luacov.stats.out 文件中。
busted已经内置了 luacov 支持,因此咱们要操心的事情就少不少。

须要往前面的 busted 命令添加 --coverage 选项:

resty -I ../src -e 'require "busted.runner"({ standalone = false })' -- --coverage --verbose test_spec.lua

为了让 luacov 可以写出覆盖报告,还须要在 .luacov 下配置:

tick = true

若是不这么作,咱们就没办法获取测试覆盖结果了。由于 luacov 默认只在程序退出时才写入覆盖报告,而咱们的 openresty
是做为后台程序运行的。设置了 tick = true 后,luacov 会按期更新覆盖报告。除了 tick,你还能够在 .luacov
中设置许多 luacov 相关的配置:
http://keplerproject.github.i...

如今已经有了份 luacov.stats.out 了,它长这样:

127:/usr/local/share/lua/5.1/busted/init.lua
0 0 0 0 4 8 4 0 4 4 0 0 10 20 10 0 10 10 0 0 36 36 0 72 0 0 0 0 36 0 36 36 36 0 36 0 36 36 72 72 36 0 0 0 0 0 0 72 0 0 36 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 92
49:/usr/local/share/lua/5.1/busted/languages/en.lua
1 0 1 0 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1
23:/usr/local/share/lua/5.1/busted/modules/files/lua.lua
1 0 1 0 0 50 100 50 1 0 0 8 1 0 0 4 4 0 0 4 1 0 1
107:/usr/local/share/lua/5.1/busted/modules/files/moonscript.lua
1 0 2 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1
...

显然,这一份不是人类可读的版本。

接着运行 luacov YOUR_SRC_DIR 能够生成出 luacov.report.out
这一份就是最终的测试覆盖率报告:

...
****0 for c, v in pairs(colorvalues) do
****0     colors[c] = makecolor(v)
      end

****0 return colors

==============================================================================
Summary
==============================================================================

File                                                              Hits Missed Coverage
...

固然这一份也不怎么“对人类友好”。若是嫌 luacov 默认生成的报告太粗糙,能够使用第三方的 reporter:
https://github.com/keplerproj...

或者,本身动手,丰衣足食:造一个 reporter 轮子。
这方面没有什么文档,只能去读luacov 默认的 reporter 实现
另一个思路是,写个脚本去解析生成的 luacov.report.out 测试报告,去生成更加可视化的版本(或者跟现有的 CI 平台对接)。

相关文章
相关标签/搜索