Testing with xUnit
Add a test project from your Godot project directory:
dnx 2dog add --xunit
dotnet test MyGame.xunitThe generated BasicTests.cs has runnable examples. Replace or extend them with tests for your game.
Your first test
Put engine tests in a HeadlessCollection and take its fixture as a constructor argument. This test adds a node to the scene tree and checks that it entered:
using Godot;
using twodog.Testing;
using twodog.Testing.Xunit;
using Xunit;
[Collection<HeadlessCollection>]
public class SceneTests(HeadlessFixture godot)
{
[Fact]
public void NodeEntersTree()
{
var node = new Node();
try
{
godot.Tree.Root.AddChild(node);
Assert.True(node.IsInsideTree());
}
finally { node.Free(); }
}
}The fixture exposes Engine, GodotInstance, Tree, Input, and Errors. Tests in the collection share these objects, so clean up what each test creates.
Choose a fixture
| Collection | Constructor argument | Use |
|---|---|---|
HeadlessCollection | HeadlessFixture godot | Scene logic and CI, without a window |
RenderingCollection | Fixture godot | Tests that need rendering |
Keep Godot collections non-parallel. You can use several; xUnit disposes one collection's engine before starting the next. Keep headless and rendering collections in separate test projects. Switching display modes within one process can crash native Godot, so 2dog's fixtures reject the switch.
Custom engine arguments
Derive from FixtureBase and define a non-parallel collection in your test project:
using twodog.Testing;
using Xunit;
public class OpenGl3Fixture()
: FixtureBase("--rendering-driver", "opengl3");
[CollectionDefinition(DisableParallelization = true)]
public class OpenGl3Collection : ICollectionFixture<OpenGl3Fixture>;The built-in collection definitions compile into your test assembly because xUnit does not discover them in an ordinary referenced DLL.
Waits, signals, and input
Use Writing engine tests for frame waits, signal expectations, cleanup, and expected errors. Use Simulating input for UI tests. Both guides work with xUnit and NUnit.
Setup and test data
| Scope | Setup | Cleanup |
|---|---|---|
| Each test | Constructor or IAsyncLifetime.InitializeAsync() | IDisposable.Dispose() or IAsyncLifetime.DisposeAsync() |
| Shared engine | A custom collection fixture | Fixture disposal |
Async lifecycle methods run on the engine's owner thread too. See xUnit's shared context guide.
Combinatorial and pairwise cases
Use TestMatrix to build managed rows for a theory's [MemberData]:
public static IEnumerable<object?[]> Cases => TestMatrix.Pairwise(
["easy", "hard"],
[1, 2, 4],
[true, false]);Add [MemberData(nameof(Cases))] to a [Theory] taking a string, int, and bool. Pairwise covers every pair of input values with a reduced set of rows. Use Combinatorial for every combination. Both are deterministic; pairwise output is not guaranteed to be the smallest possible set.
These are method inputs. For different engine configurations across an entire class, use separate fixture types and non-parallel collections.
Test data runs before the engine
Godot types such as NodePath and StringName in MemberData can crash discovery. Pass primitive values or set DisableDiscoveryEnumeration = true. See xUnit test discovery.
Runner settings
2dog keeps the tests, async continuations, and fixture disposal on the engine's owner thread. Keep Godot access on that thread; avoid Task.Run and ConfigureAwait(false) around it.
Custom entry points and test frameworks
On Windows, the collection thread is STA. 2dog also supplies the comctl32 v6 manifest unless you set your own ApplicationManifest.
On macOS, windowed Godot needs the process main thread. 2dog wraps xUnit's generated entry point to run Godot collections there. A project with its own entry point should run xUnit through GodotTestThread.RunMain.
To use another xUnit test framework, set <TwoDogGodotTestThread>false</TwoDogGodotTestThread>. This also removes the entry-point wrapper. For windowed macOS fixtures, your entry point must call GodotTestThread.RunMain and your framework must run collections through GodotTestThread.Run.
Adding packages by hand
The scaffold sets this up for you. For an existing test project:
dotnet add package 2dog.xunit
dotnet add package xunit.v3
dotnet add package Microsoft.NET.Test.Sdk
dotnet add package xunit.runner.visualstudio2dog.xunit brings in 2dog.engine. See xUnit configuration for game paths and Editor bindings.