gamedev-unity/skills/unity-skills/skills/test/SKILL.md
Run Unity Test Runner operations asynchronously — run/discover/list/cancel tests, poll job results, and create test templates. Use when running EditMode/PlayMode tests, discovering or listing tests, polling async test results, or scaffolding test files, even if the user just says "跑测试" or "单元测试". 异步执行 Unity Test Runner 操作(运行/发现/列出/取消测试、轮询任务结果、创建测试模板);当用户要运行 EditMode/PlayMode 测试、发现或列出测试、轮询异步测试结果、或生成测试文件时使用。
npx skillsauth add bernatmv/ai-rules unity-testInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Run and manage Unity tests.
Approval: 只读 skill(test_get_result / test_list / test_discover_get_result / test_get_last_result / test_list_categories / test_smoke_skills / test_get_summary,标 SkillMode.SemiAuto)直接执行;执行/发现/创建型 skill(test_run / test_run_by_name / test_discover_start / test_cancel / test_create_editmode / test_create_playmode,默认 SkillMode.FullAuto)需用户 grant,grant 后服务端一步执行返结果(job 立即排进队列)。
Auto / Bypass: 直接执行。
本模块有 4 个 NeverInSemi skill(按 IsForbiddenInSemi 自动判定):
MayEnterPlayMode = true: test_run、test_run_by_nameMayTriggerReload = true: test_create_editmode、test_create_playmode(同时标 MutatesAssets = true)Approval 模式下这 4 个返 MODE_FORBIDDEN,仅 Bypass 或 Allowlist 命中可绕过。注意:test_run(testMode="PlayMode") / test_run_by_name 会让 Unity TestRunner 切入 PlayMode;test_create_editmode / test_create_playmode 落盘新的 .cs 文件后会触发 Domain Reload。
异步约定:test_run / test_run_by_name / test_discover_start / test_create_* 立即返回 jobId;用 test_get_result(jobId) / test_discover_get_result(jobId) 轮询;Unity TestRunner 串行化,正在跑测试时不要再起第二个 test_run。
DO NOT (common hallucinations):
test_run_all does not exist → use test_run or test_run_by_nametest_create_template does not exist → use test_create_editmode or test_create_playmodetest_get_status does not exist → use test_get_result with jobId from test runjobId, poll with test_get_result(jobId)test_run while another test job is still activeunity_skills.get_skills(category="Test") or GET /skills/schema for exact signatures instead of guessing from memoryRouting:
debug module's debug_check_compilationtest_create_editmode / test_create_playmode, then modify via script moduletest_smoke_skills, which uses transient probes to avoid polluting workflow/batch persistencetest_listList available tests via Unity Test Runner async discovery. Returns pendingDiscovery=true + discoveryJobId on first call (cache miss) — poll test_discover_get_result(jobId) then retry test_list.
Parameters:
testMode (string, optional): EditMode or PlayMode. Default: EditMode.limit (int, optional): Max tests to list. Default: 100.Returns: { success, testMode, count, tests, pendingDiscovery, discoveryJobId, discoveryStatus }
test_runRun Unity tests asynchronously. Returns a jobId immediately; poll with test_get_result(jobId).
Parameters:
testMode (string, optional): EditMode or PlayMode. Default: EditMode.
Returns: { success, status, jobId, kind, testMode, filter, message }test_get_resultGet the result of a test run. Parameters:
jobId (string, required): Job ID from test_run / test_run_by_name.Returns: { success, jobId, status, totalTests, passedTests, failedTests, skippedTests, inconclusiveTests, otherTests, failedTestNames, elapsedSeconds, resultSummary, error }
test_cancelCancel a running test job if supported (Unity TestRunner has no hard cancel — best-effort). Parameters:
jobId (string, required): Job ID to cancel.Returns: { success, jobId, status, cancelled, note, warnings }
test_discover_startStart asynchronous Unity Test Runner discovery and return a discovery jobId. Use this directly when you want explicit control over discovery; otherwise test_list / test_list_categories will trigger it on cache miss.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | testMode | string | No | EditMode | EditMode or PlayMode |
Returns: { success, status, jobId, kind, testMode, message }
test_discover_get_resultGet the result of an asynchronous Unity Test Runner discovery job.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | jobId | string | Yes | - | Discovery job ID | | limit | int | No | 100 | Max tests to return |
Returns: { success, jobId, status, testMode, discoveryMode, count, tests, error }
test_run_by_nameRun specific tests by class or method name.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | testName | string | Yes | - | Test class or method name to run | | testMode | string | No | EditMode | EditMode or PlayMode |
Returns: { success, jobId, testName, testMode }
test_get_last_resultGet the most recent test run result.
No parameters.
Returns: { jobId, status, total, passed, failed, skipped, inconclusive, other, failedNames }
test_list_categoriesList test categories via Unity Test Runner async discovery. Same cache-miss / poll pattern as test_list.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | testMode | string | No | EditMode | EditMode or PlayMode |
Returns: { success, count, categories, pendingDiscovery, discoveryJobId, discoveryStatus }
test_smoke_skillsRun a reusable smoke test across registered skills.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | category | string | No | - | Only test one skill category | | nameContains | string | No | - | Filter skills by partial name | | excludeNamesCsv | string | No | - | Comma-separated skill names to exclude | | executeReadOnly | bool | No | true | Execute safe read-only skills directly | | includeMutating | bool | No | true | Include mutating skills via dryRun smoke testing | | limit | int | No | 0 | Max skills to inspect; 0 means all |
Returns: { success, totalSkills, executedCount, dryRunCount, failureCount, results }
test_create_editmodeCreate an EditMode test script template. Writes the .cs file synchronously and returns a compile-monitor jobId; the script create will trigger a Domain Reload, so the server may be temporarily unavailable — serverAvailability carries the transient-unavailable hint.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | testName | string | Yes | - | Name of the test class to create | | folder | string | No | Assets/Tests/Editor | Folder path for the test script |
Returns: { success, status, path, testName, jobId, serverAvailability }
test_create_playmodeCreate a PlayMode test script template. Writes the .cs file synchronously and returns a compile-monitor jobId; same Domain Reload + transient-unavailable note as test_create_editmode.
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | testName | string | Yes | - | Name of the test class to create | | folder | string | No | Assets/Tests/Runtime | Folder path for the test script |
Returns: { success, status, path, testName, jobId, serverAvailability }
test_get_summaryGet aggregated test summary across all runs.
No parameters.
Returns: { success, totalRuns, completedRuns, totalPassed, totalFailed, totalSkipped, totalInconclusive, totalOther, allFailedTests }
import unity_skills, time
# Run tests and poll for result (async pattern required)
result = unity_skills.call_skill("test_run", testMode="EditMode")
job_id = result["jobId"]
# Poll until done (test_* skills are async)
for _ in range(30):
status = unity_skills.call_skill("test_get_result", jobId=job_id)
if status.get("status") == "Completed":
print(f"Passed: {status['totalPassed']}, Failed: {status['totalFailed']}")
break
time.sleep(2)
Exact names, parameters, defaults, and returns are defined by GET /skills/schema or unity_skills.get_skill_schema(), not by this file.
development
Keyword research and validation with real search-demand data — never ship keywords from intuition alone. Probes Google Autocomplete per language (free, no account) to prove demand and discover the exact phrasing people type, checks SERPs for winnability, and uses Keyword Planner/Ahrefs/Semrush exports when the user has access. Activates when: choosing or reviewing SEO keywords, meta keywords, page titles, article topics or slugs, landing page copy targeting search, App Store/ASO keyword fields, multilingual keyword sets, 'what should we rank for', 'keyword analysis', or auditing why a page doesn't rank. Also invoke it as a validation pass whenever another skill or task produces a keyword list.
development
Automate YooAsset hot-update and asset bundles — build bundles, run Editor simulate builds, manage Collector groups, analyze BuildReport, and validate runtime. Use when building or simulating YooAsset bundles, configuring collectors, or validating hot-update assets, even if the user just says "热更" or "打AB包". 自动化 YooAsset 热更新与资源包(构建 bundle、编辑器模拟构建、管理 Collector 分组、分析 BuildReport、运行时校验);当用户要构建或模拟 YooAsset 资源包、配置 collector、或校验热更资源时使用。
development
Source-anchored design rules for YooAsset v2.3.18 — initialization, default-package shortcuts, play modes, asset handles, loading, updates, filesystem, build, and pitfalls. Use when writing or reviewing YooAsset code, initializing packages, loading assets via handles, setting up hot-update/download, or choosing a play mode, even if the user just says "热更" or "资源包". 为 YooAsset v2.3.18 提供源码锚定的设计规则(初始化、默认包快捷方式、运行模式、资源句柄、加载、更新、文件系统、构建、陷阱);当用户要编写或审查 YooAsset 代码、初始化 package、用句柄加载资源、配置热更/下载、或选择运行模式时使用。
data-ai
Last-resort guidance for safely hand-editing Unity serialized YAML (.unity/.prefab/.asset/.meta/ProjectSettings) — reference/fileID repair, GUID safety, and merge-conflict fixes. Use when REST cannot reach the change and YAML must be hand-edited — fixing m_Script GUIDs, broken fileID references, .meta files, or merge conflicts, even if the user just says "场景文件打不开" or "引用丢了". 安全手编 Unity 序列化 YAML(.unity/.prefab/.asset/.meta/ProjectSettings)的最后手段(引用/fileID 修复、GUID 安全、合并冲突修复);当 REST 无法触达、必须手编 YAML 时使用——修复 m_Script GUID、断裂 fileID 引用、.meta 文件或合并冲突。