Lua 测试与工具
1. 单元测试
1.1 单元测试的概念
单元测试是对程序中的最小可测试单元(如函数、模块)进行验证的过程。通过单元测试,可以确保代码的正确性和稳定性。
1.2 Lua 单元测试框架
Lua 社区提供了多个单元测试框架,常用的包括:
- Busted:功能强大的测试框架,支持行为驱动开发(BDD)。
- LuaUnit:简单易用的测试框架,适合小型项目。
- Telescope:轻量级测试框架,支持并行测试。
1.3 使用 Busted 进行单元测试
Busted 是一个功能丰富的测试框架,支持多种测试风格和报告格式。
1.3.1 安装 Busted
luarocks install busted
1.3.2 编写测试用例
-- mymodule.lua
local M = {}
function M.add(a, b)
return a + b
end
return M
-- test_mymodule.lua
describe("MyModule", function()
it("should add two numbers", function()
local mymodule = require("mymodule")
assert.are.equal(3, mymodule.add(1, 2))
end)
end)
1.3.3 运行测试
busted test_mymodule.lua
1.4 使用 LuaUnit 进行单元测试
LuaUnit 是一个简单易用的测试框架,适合小型项目。
1.4.1 安装 LuaUnit
luarocks install luaunit
1.4.2 编写测试用例
-- mymodule.lua
local M = {}
function M.add(a, b)
return a + b
end
return M
-- test_mymodule.lua
luaunit = require('luaunit')
TestMyModule = {}
function TestMyModule:testAdd()
local mymodule = require("mymodule")
luaunit.assertEquals(mymodule.add(1, 2), 3)
end
os.exit(luaunit.LuaUnit.run())
1.4.3 运行测试
lua test_mymodule.lua
1.5 使用 Telescope 进行单元测试
Telescope 是一个轻量级测试框架,支持并行测试。
1.5.1 安装 Telescope
luarocks install telescope
1.5.2 编写测试用例
-- mymodule.lua
local M = {}
function M.add(a, b)
return a + b
end
return M
-- test_mymodule.lua
local telescope = require("telescope")
local mymodule = require("mymodule")
telescope.test("add", function()
telescope.assert(mymodule.add(1, 2) == 3)
end)
telescope.run()
1.5.3 运行测试
lua test_mymodule.lua
2. 常用开发工具
2.1 Luarocks
Luarocks 是 Lua 的包管理工具,用于安装和管理 Lua 模块。
2.1.1 安装 Luarocks
- 在 Linux 上:
sudo apt-get install luarocks - 在 macOS 上:
brew install luarocks
2.1.2 使用 Luarocks 安装模块
luarocks install luasocket
2.1.3 使用 Luarocks 管理模块
- 列出已安装的模块:
luarocks list - 卸载模块:
luarocks remove luasocket
2.2 LuaCheck
LuaCheck 是一个静态代码分析工具,用于检测 Lua 代码中的潜在问题。
2.2.1 安装 LuaCheck
luarocks install luacheck
2.2.2 使用 LuaCheck
luacheck mymodule.lua
2.3 ZeroBrane Studio
ZeroBrane Studio 是一个轻量级的 Lua 集成开发环境(IDE),支持调试、代码补全等功能。
2.3.1 安装 ZeroBrane Studio
- 下载并解压 ZeroBrane Studio:
wget https://download.zerobrane.com/ZeroBraneStudioEduPack-1.90-linux.sh bash ZeroBraneStudioEduPack-1.90-linux.sh
2.3.2 使用 ZeroBrane Studio
- 打开 ZeroBrane Studio,加载 Lua 项目。
- 使用调试功能设置断点、单步执行等。
2.4 LuaFormatter
LuaFormatter 是一个 Lua 代码格式化工具,用于统一代码风格。
2.4.1 安装 LuaFormatter
luarocks install --server=https://luarocks.org/dev luaformatter
2.4.2 使用 LuaFormatter
lua-format mymodule.lua -i
3. Mock 与 Stub 机制
在测试依赖外部系统(数据库、HTTP API、文件系统)的模块时,使用 Mock 对象隔离被测单元:
-- 手动 mock:替换全局函数
local original_http_get = _G.http.get
function _G.http.get(url)
return { status = 200, body = '{"id": 1}' } -- 固定返回值
end
-- 测试后恢复
_G.http.get = original_http_get
更健壮的方案是使用 lua-mock 库:
luarocks install luamock
local mock = require("luamock")
local mymodule = require("mymodule")
describe("with mocked HTTP", function()
it("parses response correctly", function()
mock.patch(mymodule, "http_get", function()
return { status = 200, body = '{"success": true}' }
end)
local result = mymodule.fetch_config()
assert.is_true(result.success)
mock.restore(mymodule, "http_get") -- 务必恢复
end)
end)
Stub 与 Mock 的区别:Stub 只提供预设返回值,Mock 还会验证函数是否被调用、调用参数是否正确。在 Lua 中通常统称为 Mock,因为实现方式相同(函数替换)。
4. TDD 实践流程
Lua 中进行测试驱动开发的完整循环:
1. 写一个失败的测试 ──► busted 输出红色
2. 写最少代码让测试通过 ──► busted 输出绿色
3. 重构代码保持测试通过 ──► 绿色
4. 回到步骤 1,实现下一个需求
TDD 在 Lua 中特别有效的原因:Lua 是动态类型语言,没有编译器做静态检查,单元测试就是最主要的质量保障手段。一个 Lua 项目的测试覆盖率不应低于 70%,核心模块(如支付、权限)应达到 90% 以上。
5. 性能分析工具
5.1 Lua 内置 profiler
local profile = require("jit.profile")
-- LuaJIT 内置火焰图生成
local function dumpstack()
profile.start("i1", function(thread, samples, vmstate)
print(profile.dumpstack(thread, "pl\n", 10))
end)
end
5.2 lua-perf:纯 Lua 轻量分析器
luarocks install lua-perf
local perf = require("perf")
perf.start()
for i = 1, 100000 do
table.insert(result, i * 2)
end
perf.stop()
-- 输出热点函数排名
for _, fn in ipairs(perf.top(10)) do
print(fn.name, fn.time_ms .. "ms", fn.calls .. " calls")
end
5.3 内存泄漏检测
Lua 的 GC 是自动的,但循环引用、全局变量累积、未释放的协程会导致内存持续增长。使用 collectgarbage("count") 监控内存波动:
local function memory_check(name, fn)
collectgarbage("collect")
local before = collectgarbage("count") -- KB
fn()
collectgarbage("collect")
local after = collectgarbage("count")
print(string.format("%s: %.1f KB", name, after - before))
end
memory_check("small_table", function()
local t = {}
for i = 1, 1000 do t[i] = i end
end)
6. CI/CD 集成
GitHub Actions 配置
# .github/workflows/lua-ci.yml
name: Lua CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
lua-version: ['5.3', '5.4', 'luajit-openresty']
steps:
- uses: actions/checkout@v4
- name: Setup Lua
uses: leafo/gh-actions-lua@v10
with:
luaVersion: ${{ matrix.lua-version }}
- name: Setup LuaRocks
uses: leafo/gh-actions-luarocks@v4
- name: Install dependencies
run: |
luarocks install --deps-only myproject-dev-1.rockspec || true
luarocks install busted
luarocks install luacheck
luarocks install luacov # 覆盖率工具
- name: Lint
run: luacheck src/ --std max --globals _ENV
- name: Run tests with coverage
run: |
busted --coverage spec/
luacov # 生成 luacov.report.out
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: luacov.report.out
flags: unittests
代码质量门禁
#!/bin/bash
# pre-commit 钩子:本地提交前强制检查
# 1. 静态检查
luacheck src/ || exit 1
# 2. 单元测试
busted spec/ || exit 1
# 3. 覆盖率阈值(核心模块 >= 70%)
lua -e "
local f = io.open('luacov.report.out', 'r')
local content = f:read('*all')
f:close()
local covered, total = content:match('Total%s+(%d+)%s+(%d+)')
local rate = tonumber(covered) / tonumber(total)
if rate < 0.70 then
print('Coverage ' .. math.floor(rate*100) .. '% < 70%, blocked')
os.exit(1)
end
print('Coverage OK: ' .. math.floor(rate*100) .. '%')
"
7. 测试策略矩阵
| 测试类型 | 工具 | 频率 | 目标 |
|---|---|---|---|
| 单元测试 | Busted / LuaUnit | 每次提交 | 覆盖所有公开函数 |
| 集成测试 | 自研脚本 | 每日 CI | 模块间接口契约 |
| 静态分析 | LuaCheck | 每次提交 | 语法错误、未定义变量、全局变量污染 |
| 性能测试 | lua-perf | 每周 | 热点路径耗时基线 |
| 覆盖率 | luacov | 每次 CI | 核心模块 ≥ 70%,工具模块 ≥ 50% |
8. 调试技巧补充
ZeroBrane Studio 的远端调试功能对游戏开发至关重要:
-- 在游戏启动脚本中加入(仅在调试模式)
if os.getenv("DEBUG") == "1" then
require("mobdebug").start("192.168.1.100", 8172)
end
| 调试方式 | 场景 | 操作 |
|---|---|---|
| 本地断点 | 单机脚本 | IDE 内 F5 启动 |
| 远端 attach | 游戏引擎 /嵌入式设备 | mobdebug TCP 连接到开发机 |
| 日志追踪 | 生产环境问题 | 结构化日志 + 会话 ID 串联 |
3. 总结
Lua 的测试与工具生态系统丰富,通过单元测试框架(如 Busted、LuaUnit、Telescope)可以确保代码的正确性。常用开发工具(如 Luarocks、LuaCheck、ZeroBrane Studio、LuaFormatter)可以提高开发效率和代码质量。掌握这些工具的使用方法,可以帮助开发者编写高效、稳定的 Lua 程序。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。