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 平臺對接)。

相關文章
相關標籤/搜索