Skip to content

xUnit Host

MyGame.tests runs tests against a real Godot engine, project, and resources. Because xUnit owns the process, an xUnit fixture owns the engine lifetime.

Use It

bash
dotnet test MyGame.tests

This page covers host anatomy

Testing with xUnit is the canonical guide to fixtures, engine arguments, collections, filtering, and CI workflows.

Capabilities

  • Tests scenes, resources, and game code against a real Godot engine.
  • Supports headless tests by default and rendered tests when needed.
  • Supports the same debug, release, and editor variants as the generic host.
  • Imports changed resources automatically before tests run.

How It Works

csharp
using Godot;
using twodog.Testing;
using twodog.Testing.Xunit;
using Xunit;

namespace MyGame.Tests;

[Collection<HeadlessCollection>]
public class BasicTests(HeadlessFixture godot)
{
    [Fact]
    public void LoadMainScene_Succeeds()
    {
        var mainScene = (string)ProjectSettings.GetSetting("application/run/main_scene", "");
        Assert.SkipWhen(mainScene == "", "No run/main_scene configured in project.godot");

        var instance = GD.Load<PackedScene>(mainScene).Instantiate();
        godot.Tree.Root.AddChild(instance);

        Assert.NotNull(instance.GetParent());
    }
}

The fixture exposes the same objects a generic host keeps in local variables:

Fixture memberConsole equivalent
godot.Enginenew Engine(...)
godot.GodotInstanceengine.Start()
godot.Treeengine.Tree

Project Setup

The shared host project is documented in Hosts. The test host references 2dog.xunit, xUnit, the test SDK, and MyGame.csproj. 2dog.xunit brings in 2dog.engine, fixtures, and collection definitions.

xml
<ItemGroup>
  <PackageReference Include="2dog.xunit" Version="4.7.2.83"/>
  <PackageReference Include="xunit.v3" Version="3.*"/>
  <PackageReference Include="xunit.runner.visualstudio" Version="3.*"/>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.*"/>
  <PackageReference Include="coverlet.collector" Version="10.*"/>
</ItemGroup>

<ItemGroup>
  <ProjectReference Include="../MyGame.csproj"/>
</ItemGroup>

<PropertyGroup>
  <GodotProjectDir>..</GodotProjectDir>
  <TwoDogVariant Condition="'$(Configuration)' == 'Debug'">debug</TwoDogVariant>
  <TwoDogVariant Condition="'$(Configuration)' == 'Editor'">editor</TwoDogVariant>
  <TwoDogRemoveDuplicateGodotAnalyzers>true</TwoDogRemoveDuplicateGodotAnalyzers>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Editor'">
  <DefineConstants>$(DefineConstants);EDITOR</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(Configuration)' == 'Editor'">
  <PackageReference Include="GodotSharpEditor" Version="4.7.2.*"/>
</ItemGroup>

GodotProjectDir enables automatic resource import, so tests see freshly imported assets. Debug, Release, and Editor configurations select the matching native variant; see Build Variants.

Limitations

The generated host sets "parallelizeTestCollections": false in xunit.runner.json: one engine runs at a time, and every test in a collection shares it. Testing with xUnit covers collections and the single-instance rule.

Remember that nodes added to the shared tree are not cleaned up automatically; QueueFree() what you create. See the known issues for Godot types in MemberData and GD.Print output.