Testing Tools¶
Dispatch and manage NUnit tests (EditMode and PlayMode). Control test execution, monitor progress, and verify test outcomes with durable run identity for reliable automation.
run_tests¶
Dispatch NUnit tests without waiting for completion. Returns immediately with a stable run_id for polling.
Parameters: - mode (string, default="EditMode") — "EditMode" | "PlayMode" - filter (string, optional) — Pipe-separated test class names (e.g., "HealthTest|DamageTest") for fast focused runs - request_id (string, optional) — Caller-supplied stable identity; reuse it when resolving an uncertain start
Output: - Success: tests-started|request_id=...|run_id=...|utf_guid=...|state=dispatched - Lost ACK: START-UNKNOWN|request_id=...|reason=... (call resolve_test_request() with same request_id)
Example:
# Start EditMode tests
ack = await run_tests(mode="EditMode")
# -> tests-started|request_id=abc123|run_id=xyz789|utf_guid=...|state=dispatched
# Run only specific failing tests (much faster)
ack = await run_tests(mode="EditMode", filter="HealthTest|DamageTest")
Use run_tests_wait() for most workflows — it polls automatically until completion.
run_tests_wait¶
Dispatch tests and block until that exact run reaches a terminal state. The preferred entry point for consumer-project MCP sessions.
Parameters: - mode (string, default="EditMode") — "EditMode" | "PlayMode" - filter (string, optional) — Pipe-separated test class names for focused runs - timeout (float, default=900.0) — Max seconds to wait - poll_interval (float, default=5.0) — Seconds between status polls - request_id (string, optional) — Caller-supplied stable identity
Returns: Reconciled terminal JSON snapshot, timeout message, protocol error, or BLOCKED reason.
Example:
# Wait for all EditMode tests
result = await run_tests_wait(mode="EditMode")
# Run specific tests with shorter timeout
result = await run_tests_wait(mode="EditMode", filter="HealthTest|DamageTest", timeout=60)
Workflow:
# 1. Run EditMode first (fast gate)
result = await run_tests_wait(mode="EditMode")
# 2. If pass, run PlayMode (requires MCP reconnect)
if "passed" in result:
result = await run_tests_wait(mode="PlayMode")
Note: Repository and disposable-worker C# verification use the standalone run_unity_tests.py runner, not this MCP poll loop.
resolve_test_request¶
Resolve a potentially lost start acknowledgment without launching another run. Use only if run_tests() returned START-UNKNOWN.
Parameters: - request_id (string, required) — The request_id from the lost ACK or START-UNKNOWN response
Returns: test-request|request_id=...|run_id=...|state=... if a run exists with that request ID, or error.
Example:
# Retry resolution if dispatch ACK was lost
status = await resolve_test_request(request_id="abc123")
# -> test-request|request_id=abc123|run_id=xyz789|state=running
# Use the resolved run_id for polling
result = await get_test_run(run_id="xyz789")
get_test_run¶
Fetch the durable JSON snapshot for one exact run. Only a reconciled state="terminal" snapshot is completion evidence.
Parameters: - run_id (string, required) — Value returned by run_tests(), run_tests_wait(), or resolve_test_request()
Returns: Complete JSON snapshot with state, outcome, manifest, test counts, and cleanup evidence.
Output includes: - state — "prepared", "dispatched", "running", "finalizing", or "terminal" - outcome — "passed", "failed", "cancelled", "incomplete", etc. (only valid when state="terminal") - expected_count, passed, failed, skipped — Test manifest and results - cleanup_complete, is_terminal — Completion proof - issues — Infrastructure errors or test problems (list of {severity, message})
Example:
# Check exact run status
snapshot = await get_test_run(run_id="xyz789")
# Verify terminal state before accepting result
if snapshot.get("state") == "terminal":
if snapshot.get("outcome") == "passed":
print("All tests passed")
else:
print(f"Tests failed: {snapshot.get('failed')} failures")
get_test_count¶
Return the total number of NUnit tests currently available in the project.
Parameters: None
Returns: Single number (string).
Example:
get_test_results¶
Legacy result facade. Prefer get_test_run() for acceptance — the durable snapshot includes lifecycle and cleanup evidence.
Parameters: - run_id (string, optional) — Specific run identity (defaults to latest)
Output: Result summary with pass/fail counts, or "pending".
Example:
get_test_progress¶
Legacy progress facade. Prefer get_test_run() for acceptance.
Parameters: - run_id (string, optional) — Specific run identity (defaults to latest)
Output: Progress summary or "pending".
Example:
cancel_test_run¶
Request cancellation of one exact run. Cancellation is asynchronous.
Parameters: - run_id (string, required) — The run to cancel
Returns: Acknowledgment; keep polling get_test_run() until the run reaches terminal state.
Example:
# Cancel an in-progress run
status = await cancel_test_run(run_id="xyz789")
# Poll until actually stopped
while True:
snapshot = await get_test_run(run_id="xyz789")
if snapshot.get("state") == "terminal":
print(f"Cancelled with outcome: {snapshot.get('outcome')}")
break
await asyncio.sleep(2)
list_test_runs¶
List recent durable runs, newest first. Diagnostic aid only — do not substitute a "latest" entry for the run_id you dispatched.
Parameters: - limit (int, default=20) — Max runs to return
Returns: List of recent run snapshots (newest first).
Example:
runs = await list_test_runs(limit=10)
# -> [{"run_id": "xyz789", "state": "terminal", "outcome": "passed"}, ...]
Common Patterns¶
| Task | Tools | Example |
|---|---|---|
| Run all EditMode tests | run_tests_wait | result = await run_tests_wait(mode="EditMode") |
| Run specific failing tests | run_tests_wait + filter | result = await run_tests_wait(mode="EditMode", filter="HealthTest") |
| Run PlayMode after EditMode passes | run_tests_wait (two calls) | First EditMode, then PlayMode if pass |
| Recover from lost start ACK | resolve_test_request | run_id = await resolve_test_request(request_id=...) |
| Poll async test run manually | run_tests + get_test_run | Use run_tests() then get_test_run(run_id=...) |
| Check terminal test result | get_test_run | Verify state="terminal" and outcome field |
Warnings:
- Start both test modes: Never run PlayMode without first running EditMode as a gate.
- Durable identity: Always use the
run_idreturned by dispatch or resolved viaresolve_test_request(). Do not poll an implicit "latest" run. - Terminal proof: Only
state="terminal"snapshots are valid completion evidence. Timeout is observational and does not mark the run complete. - Repository verification: Use standalone
run_unity_tests.pyrunner for Biome repository and disposable-worker C# tests, not this MCP poll loop.
See also: Diagnostics for compile checks before tests, Runtime Tools for PlayMode state assertions.