关于 roblox-testing
面向 Roblox 游戏开发者的测试指南,介绍如何组织可测试代码、使用 Jest Lua 编写测试,以及在 Studio、CLI 和云端执行测试。还包含多人游戏测试方法与发布前检查清单。
主要功能
- 01将游戏逻辑拆分为纯模块
- 02通过依赖注入使用测试替身
- 03使用 Jest Lua 编写和运行测试
- 04在 Studio、CLI 或云端执行测试
- 05用多客户端和设备模拟器进行试玩
- 06按发布前清单检查功能
适用场景
- 01验证库存、伤害或商店逻辑
- 02用内存替身测试数据服务
- 03在 CI 中运行无头测试
- 04检查多人同步与跨设备体验
MCP 相关
介绍 Studio MCP 的 `execute_luau`、`start_stop_play` 和 `get_console_output`,供 Agent 对打开的场景运行测试与试玩。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ el4cteo/ roblox-skills --skill roblox-testing安装后在 Claude Code 中输入/roblox-testing确认已加载。
附带文件references/cloud-test-runner.md
详细介绍
测试 Roblox 游戏
先让代码具备可测试性
- 将游戏规则放在纯 ModuleScripts 中(输入 → 输出,不使用服务、不让出执行权):伤害公式、 背包操作、商店定价、状态机、序列化、校验。这些都能轻松测试。
- 服务通过参数接收依赖(依赖注入),这样测试就可以传入伪对象,
而不必使用真实的 DataStore、远程事件或
Players。
--!strict
-- ReplicatedStorage/Shared/Inventory.luau — pure logic, no Roblox services.
export type Inventory = { [string]: number }
local Inventory = {}
function Inventory.add(inventory: Inventory, itemId: string, amount: number, maxStack: number): (boolean, string?)
if amount <= 0 or amount ~= math.floor(amount) then
return false, "invalid amount"
end
local current = inventory[itemId] or 0
if current + amount > maxStack then
return false, "stack full"
end
inventory[itemId] = current + amount
return true, nil
end
return Inventory
--!strict
-- A service that receives its data store, so tests can inject an in-memory fake.
type Store = {
GetAsync: (self: Store, key: string) -> any,
SetAsync: (self: Store, key: string, value: any) -> (),
}
local function newCurrencyService(store: Store)
local service = {}
function service.award(userId: number, amount: number): number
local key = `User_{userId}`
local balance = (store:GetAsync(key) or 0) + amount
store:SetAsync(key, balance)
return balance
end
return service
end
-- In tests: a fake store backed by a table.
local fakeData: { [string]: any } = {}
local fakeStore: Store = {
GetAsync = function(_self, key)
return fakeData[key]
end,
SetAsync = function(_self, key, value)
fakeData[key] = value
end,
}
print(newCurrencyService(fakeStore).award(1, 50))
return newCurrencyService
Jest Lua(推荐框架)
Jest 的移植版(describe/it/expect、模拟、快照)。TestEZ 已过时;Jest Lua 3 包含迁移指南。
# wally.toml
[dev-dependencies]
Jest = "jsdotlua/[email protected]"
JestGlobals = "jsdotlua/[email protected]"
-- src/shared/Inventory.spec.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local JestGlobals = require(ReplicatedStorage.DevPackages.JestGlobals)
local Inventory = require(ReplicatedStorage.Shared.Inventory)
local describe, it, expect = JestGlobals.describe, JestGlobals.it, JestGlobals.expect
describe("Inventory.add", function()
it("stacks items up to the limit", function()
local inventory = {}
expect(Inventory.add(inventory, "Apple", 3, 5)).toBe(true)
expect(inventory.Apple).toBe(3)
local ok, reason = Inventory.add(inventory, "Apple", 3, 5)
expect(ok).toBe(false)
expect(reason).toBe("stack full")
end)
it("rejects fractional amounts", function()
expect(Inventory.add({}, "Apple", 0.5, 5)).toBe(false)
end)
end)
- 在每个测试根目录中放置一个
jest.config.luau(return { testMatch = { "**/*.spec" } })。 - Runner 脚本(Studio 命令栏、测试 place 的 Script,或云任务):
require(ReplicatedStorage.DevPackages.Jest).runCLI(root, { ci = true }, { root }):awaitStatus()→ 检查result.results.numFailedTests。 - 本地运行 Jest Lua 3 时,Studio 的
ClientAppSettings.json中需要启用快速标志FFlagEnableLoadModule = true。 - 仅在测试构建中将
DevPackages映射到 DataModel(使用单独的 Rojo 项目,例如test.project.json)。
测试运行位置
| Runner | 用途 |
|---|---|
| Studio(命令栏 / 测试 Script) | 本地开发。 |
Studio MCP(execute_luau、start_stop_play、get_console_output) | Agent 在打开的 place 中运行测试和游玩测试——参见 roblox-studio-mcp。 |
run-in-roblox | 会打开 Studio、运行脚本并传递输出的 CLI(需要安装 Studio;适用于本地 / Windows / mac CI Runner)。 |
| Open Cloud Luau Execution | 无头 CI:在已发布 place 版本对应的真实 Roblox 服务端中运行脚本。 |
| Lune | 不依赖 Roblox API 的纯 Luau 模块(速度快,可在任何环境运行)。 |
Open Cloud Luau Execution 流程(详情和脚本见:references/cloud-test-runner.md):
- 构建测试 place(
rojo build test.project.json -o test.rbxl),并将其上传为专用测试 place 的已保存版本。 - 使用
{ "script": ... }调用POST /cloud/v2/universes/{u}/places/{p}/versions/{v}/luau-execution-session-tasks(API 密钥范围universe.place.luau-execution-session:write;每位密钥所有者的速率限制约为每分钟 5 个任务)。 - 轮询任务
path,直到state变为COMPLETE/FAILED;读取output.results和.../logs。
在 Studio 中进行游玩测试
- Test 选项卡:Play(你和服务端)、Run(仅服务端,无角色)、Clients and Servers(使用 2 个或更多玩家测试复制、交易和 PvP)。大多数多人游戏 Bug 只有在客户端数量 ≥ 2 时才会出现。
- Device Simulator 工具栏(Test → Device Simulator,视口顶部):在游玩测试期间切换手机 / 平板 / 主机,并显示触控控制提示;其 Network Simulator 可模拟 3G / 4G / Wi-Fi / 有线网络,或自定义的入站 / 出站延迟、抖动和丢包,用于测试延迟补偿及加载 / 重试体验。 使用 Controller Emulator 模拟手柄输入。
- 流式加载:使用
StreamingTargetRadius = 64进行测试。 - Studio 中的 DataStore:启用 API 访问,并使用单独的测试 universe 或数据存储名称。
功能发布前的 QA 检查清单
- 玩家在功能启动后才加入(晚加入玩家)或重生后,功能仍可正常运行。
- 支持 2 个或更多客户端;服务端状态具有权威性;UI 在所有客户端上更新。
- 玩家在操作中途离开(交易、购买、回合)时,不会留下损坏的状态或泄漏的表。
- Remotes 在被垃圾请求或发送格式错误的参数时会被拒绝。
- 数据可以保存并重新加载(重新加入),包括在不同 place 之间传送。
- 手机、手柄和键盘均可正常使用;UI 在手机上清晰可读。
- Output(服务端和客户端)中没有错误或警告。
相关技能
roblox-tooling(CI、Rojo)、roblox-studio-mcp(由 Agent 驱动的游玩测试)、roblox-open-cloud、
roblox-code-review。
评论
支持 Markdown