roblox-testing について
Roblox ゲーム開発者向けのテストガイドです。テストしやすいコード構成、Jest Lua によるテスト、Studio・CLI・クラウドでの実行方法を扱い、マルチプレイヤーのテスト方法とリリース前チェックリストも示します。
主な機能
- 01ゲームロジックを純粋なモジュールに分離
- 02依存性注入とテスト用フェイクの利用
- 03Jest Lua によるテストの作成と実行
- 04Studio、CLI、クラウドでのテスト実行
- 05複数クライアントとデバイスシミュレーターでのプレイテスト
- 06リリース前チェックリストで機能を確認
活用シーン
- 01インベントリ、ダメージ、ショップのロジック検証
- 02インメモリのフェイクでデータサービスをテスト
- 03CI でヘッドレステストを実行
- 04マルチプレイヤー同期とデバイス対応を確認
MCP 対応
Studio MCP の `execute_luau`、`start_stop_play`、`get_console_output` を使い、エージェントが開いているプレースでテストやプレイテストを実行する方法を紹介します。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ el4cteo/ roblox-skills --skill roblox-testingインストール後、Claude Code で/roblox-testingを入力して読み込まれたか確認します。
同梱ファイルreferences/cloud-test-runner.md
詳細
Roblox ゲームのテスト
まずテスト可能なコードにする
- ゲームルールは純粋な ModuleScript(入力 → 出力、サービスなし、yield なし)にまとめます。ダメージ計算式、インベントリ操作、ショップの価格設定、状態マシン、シリアライズ、検証などが該当します。これらは簡単にテストできます。
- サービスは依存関係をパラメーターとして受け取るようにします(依存性注入)。そうすれば、テストでは実際の 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" } })を配置します。 - ランナースクリプト(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を使用)。
テストの実行場所
| ランナー | 用途 |
|---|---|
| Studio(コマンドバー/テスト用 Script) | ローカル開発。 |
Studio MCP(execute_luau、start_stop_play、get_console_output) | 開いている place でテストやプレイテストを実行するエージェント向け。roblox-studio-mcp を参照してください。 |
run-in-roblox | Studio を開いてスクリプトを実行し、出力をパイプする CLI(Studio のインストールが必要。ローカル/Windows/mac CI ランナーに対応)。 |
| 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 の Saved バージョンとしてアップロードします。 { "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 をテストします。マルチプレイヤーのバグのほとんどは、クライアントが 2 つ以上でないと発生しません。
- Device Simulator ツールバー(Test → Device Simulator、ビューポート上部):プレイテスト中にスマートフォン/タブレット/コンソールを切り替え、タッチ操作のヒントを確認できます。Network Simulator では 3G/4G/Wi-Fi/有線、またはカスタムの受信/送信レイテンシ、ジッター、パケット損失を適用して、ラグ補正や読み込み/再試行時の UX をテストできます。ゲームパッド入力には Controller Emulator を使用します。
- ストリーミング:
StreamingTargetRadius = 64に設定してテストします。 - Studio での DataStore:API アクセスを有効にし、別のテスト用ユニバースまたはデータストア名を使用します。
機能リリース前の QA チェックリスト
- 機能の開始後に参加したプレイヤー(途中参加者)や、リスポーン後のプレイヤーでも正常に動作する。
- クライアントが 2 つ以上でも動作する。サーバーが状態を管理し、すべてのクライアントで UI が更新される。
- プレイヤーがアクション中(トレード、購入、ラウンド中)に退出しても、状態の破損やテーブルのリークが発生しない。
- リモートに対するスパム送信や、不正な引数を含む送信が拒否される。
- テレポートによる place 間の移動も含め、データが保存され、再読み込みされる。
- モバイル、ゲームパッド、キーボードのすべてで動作し、スマートフォンでも UI が読みやすい。
- Output にエラーや警告がない(サーバーとクライアントの両方)。
関連スキル
roblox-tooling(CI、Rojo)、roblox-studio-mcp(エージェント主導のプレイテスト)、roblox-open-cloud、roblox-code-review。
コメント
Markdown 対応