《Lua快速入门》13.Lua测试与工具

Lua 测试与工具介绍,包括单元测试框架、代码覆盖率工具等。

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 程序。


继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「lua」更多文章

  1. Lua 时间日期处理与时区
  2. Lua 在嵌入式与 IoT 中的开发实践
  3. Lua 与 WebAssembly 互操作