Scenarios

Agents prove the change works.

"Done!" is not evidence. Subclass TddScenario, write Run(), and any player executes it inside a real Play Mode session, headless or live, host or client, and reports pass or fail.

public class HostJoinTest : TddScenario
{
    public override IEnumerator Run()
    {
        if (IsHost) yield return StartHost();
        else        yield return JoinAndMove();
    }
}

How an agent checks its own work

An agent that writes game code has no natural way to check its own work. Compiling proves nothing about behaviour, and unit tests around a MonoBehaviour usually end up testing scaffolding. So the agent reports success, and you find out later whether it was right.

What closes that gap is letting the agent run the game and observe it. That is what a scenario is: a small piece of C# that plays out a situation in a real Play Mode session and produces a verdict.

The API

There is one abstract member.

public abstract class TddScenario : MonoBehaviour
{
    public static int    PlayerIndex { get; }
    public static int    PlayerCount { get; }
    public static string Role        { get; }
    public static bool   IsHost      => Role == "host";

    public virtual float StallTimeoutSeconds => 30f;

    public abstract IEnumerator Run();
}

Subclass it, implement Run() as a coroutine, and the runner does the rest: it finds the type, spawns it into the loaded scene, marks it DontDestroyOnLoad, and drives the coroutine to completion.

One file, every side of the match

PlayerIndex, PlayerCount, Role and IsHost are set on each instance before Run() starts. That means one scenario describes the whole multiplayer situation and each client branches to its own part: the host starts a match, the clients join and move, and every side writes its own result.

Output an agent can read

Scenarios log through LogTdd, which writes per-tag files stamped with frame number and real time:

[F:412 T:6.883] client-2 position desync 0.42m

The agent reads those back with unity_tdd_logs from the Unity MCP server, alongside each side's pass/fail result. Frame stamps matter more than they look: for a desync or a race, "which frame" is usually the entire question.

Failures that explain themselves

The most useless test result is a scenario that hangs forever. A coroutine host watches for progress, either a completed step or a new LogTdd line, and aborts the run if nothing happens for longer than the stall limit. The default is 30 seconds, overridable per scenario with StallTimeoutSeconds for legitimately long quiet waits.

An aborted run reports more than "timeout": how long there was no progress, which step it wedged on, what it was waiting for (down to WaitForSeconds(2.0)), the frame number, and up to eight errors logged during the run. An exception thrown outside the scenario coroutine is very often what broke the condition it was waiting on.

An agent debugging from "the test timed out" will guess. An agent reading "wedged after step 3, waiting on WaitForSeconds(2.0) for 34s at frame 1902, with a NullReferenceException logged in between" will usually fix it on the first try.

Where they run

  • In the editor session you are already streaming, live, with the game visible.
  • In Headless NoGraphics, where the loop runs thousands of ticks a second and a long scenario finishes in seconds.
  • Across the multiplayer grid, every client running its own side simultaneously.
  • In a built player, where the scenario quits the process with an exit code when it finishes.

unity_open_scene opens a scene, enters Play Mode and runs a named scenario in one call, so the whole verification is a single tool call for the agent.

The specifics.

Surface

One methodSubclass TddScenario, implement Run() as a coroutine.

Per player

IsHost · PlayerIndexPlus PlayerCount and Role, set before the run starts.

Logging

LogTddPer-tag files stamped with frame and real time.

Stall limit

30s, overridableAborts with the wedged step, the pending yield and recent errors.

Straight answers

Scenario questions.

How is this different from Unity Test Framework?

UTF runs tests in its own harness. A scenario runs inside the session you're already streaming, on every multiplayer client at once, so it can express "host starts a match, client joins and moves, both agree on the position".

Does it replace my existing tests?

No. Keep your unit tests where they are. Scenarios cover the part unit tests are bad at: behaviour, over time, in a real session, across several clients.

How does a scenario know which player it is?

PlayerIndex, PlayerCount, Role and IsHost are set on each instance before Run() starts.

What if a scenario hangs?

A stall detector aborts it and reports the wedged step, what it was waiting on, the frame, and any errors logged during the run. The 30-second default is overridable via StallTimeoutSeconds.

How does the agent read results?

Each side writes its own pass/fail result, and unity_tdd_logs returns the frame-stamped LogTdd output from the run.

Can scenarios run in a built player?

Yes. In a player the scenario quits the process with an exit code when it finishes, so it works in an ordinary automated run too.

Related

Make the agent show its work.

The running game reports pass or fail, so you don't have to take the agent's word for it.

7-day free trial (no card required)macOS & Windows