Table of Contents

Unit Tests

Unit tests in Stride are set up like any other unit tests in dotnet, you create a new project specifically for unit tests, then write your tests in different C# files.

Here's a bare-bone project to get you started: YOUR_PROJECT_NAME.Windows.Tests.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <!-- Change this framework to match the one specified in your *.Windows.csproj -->
        <TargetFramework>net8.0-windows</TargetFramework>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <OutputType>WinExe</OutputType>

        <OutputPath>..\Bin\Tests\Windows\$(Configuration)\</OutputPath>
        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>

        <!-- Force msbuild to check to rebuild this assembly instead of letting VS IDE guess -->
        <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
        
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>

        <IsPackable>false</IsPackable>
        <IsTestProject>true</IsTestProject>
    </PropertyGroup>
    <ItemGroup>
        <!-- Add a reference to your game project here, do not reference the windows project here -->
        <PackageReference Include="coverlet.collector" Version="6.0.0"/>
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
        <PackageReference Include="xunit" Version="2.5.3"/>
        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
    </ItemGroup>
    <ItemGroup>
        <Using Include="Xunit"/>
    </ItemGroup>
</Project>

And an example C# file:

using Stride.Engine;

public class Tests
{
    [Fact]
    public void MyBareboneTest()
    {
        Assert.NotEqual(1, 2);
    }
    
    [Fact]
    public void MyGameTest()
    {
        RunGameTest(async (game, scene) =>
        {
            var myEntity = new Entity();
            scene.Entities.Add(myEntity);

            Assert.NotEmpty(scene.Entities);

            await game.Script.NextFrame(); // Wait one frame if you need to

            myEntity.Scene = null;

            Assert.Empty(scene.Entities);
        });
    }

    /// <summary>
    /// Run the given function within a game, providing support for tests requiring ECS, physics simulation, graphics and others.
    /// </summary>
    private static void RunGameTest(Func<Game, Scene, Task> asyncFunction)
    {
        using var game = new Game();
        
        // Fixed time step to reduce framerate discrepancies
        game.IsFixedTimeStep = true;
        game.IsDrawDesynchronized = false;
        game.TargetElapsedTime = TimeSpan.FromTicks(10000000 / 60); // 60hz, 60fps
        
        game.Script.AddTask(async () =>
        {
            await asyncFunction(game, game.SceneSystem.SceneInstance.RootScene);
            game.Exit();
        });
        game.Run();
    }
}