Chickensoft.GoDotTest 1.1.0-beta6

This is a prerelease version of Chickensoft.GoDotTest.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Chickensoft.GoDotTest --version 1.1.0-beta6                
NuGet\Install-Package Chickensoft.GoDotTest -Version 1.1.0-beta6                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Chickensoft.GoDotTest" Version="1.1.0-beta6" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Chickensoft.GoDotTest --version 1.1.0-beta6                
#r "nuget: Chickensoft.GoDotTest, 1.1.0-beta6"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Chickensoft.GoDotTest as a Cake Addin
#addin nuget:?package=Chickensoft.GoDotTest&version=1.1.0-beta6&prerelease

// Install Chickensoft.GoDotTest as a Cake Tool
#tool nuget:?package=Chickensoft.GoDotTest&version=1.1.0-beta6&prerelease                

GoDotTest

Chickensoft Badge Discord line coverage branch coverage

C# test runner for Godot. Run tests from the command line, collect code coverage, and debug tests in VSCode.

For Godot 3.x, use versions <= 1.0.0. For Godot 4.x, use versions > 1.0.0.

Installation

Find the latest version of GoDotTest on nuget.

Add the latest version of GoDotTest to your *.csproj file. Make sure to replace *VERSION* with the latest version.

<ItemGroup>
  <PackageReference Include="Chickensoft.GoDotTest" Version="*VERSION*" />
</ItemGroup>

You can use GoDotTest with C# 10 and Godot to run, debug, and collect code coverage for your project inside Godot.

For C# 10 to work, you need the dotnet 6 SDK installed. See what you have installed with dotnet --info. On mac, Godot 3 can have trouble finding .NET 6 if you have older SDK's installed, due to the dotnet path search order. There are also a few work-arounds available.

Examples

Here's a simple test which does absolutely nothing. It can use the TestScene node available to it from its base class to manipulate the scene tree, if needed.

using Godot;
using GoDotTest;

public class ExampleTest : TestClass {
  private readonly ILog _log = new GDLog(nameof(ExampleTest));

  public ExampleTest(Node testScene) : base(testScene) { }

  [SetupAll]
  public void SetupAll() => _log.Print("Setup everything");

  [Setup]
  public void Setup() => _log.Print("Setup");

  [Test]
  public void Test() => _log.Print("Test");

  [Cleanup]
  public void Cleanup() => _log.Print("Cleanup");

  [CleanupAll]
  public void CleanupAll() => _log.Print("Cleanup everything");
}

Below is the test execution output GoDoTest shows for its own tests:

test output

Setup

You can debug tests in Godot from Visual Studio Code. To do this, you will need to specify the GODOT environment variable for the following launch configurations and scripts to work correctly. The GODOT variable should point to the path of the Godot executable.

You will need to specify the GODOT environment variable in your .zshrc or .bash_profile file (or set it up manually on Windows).

# Dotnet
export DOTNET_CLI_TELEMETRY_OPTOUT=1 # Disable analytics
DOTNET_ROOT="/usr/local/share/dotnet"
# Mono
export PATH="/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono:$PATH"
export PATH="$HOME/.dotnet/tools:$PATH"
# For dotnet 6 SDK:
export PATH="/usr/local/share/dotnet:/usr/local/share/dotnet/sdk:$PATH"
# Godot
# Path go Godot executable, on mac it might look like this:
export GODOT="/Applications/Godot.app/Contents/MacOS/Godot"

Debugging

The following launch.json file provides launch configurations to debug the game, debug all the tests, or debug the currently open test in Visual Studio Code. To debug the currently open test, make sure the class name of the test matches the file name, as is typical in C#.

Godot 3.x Launch Configurations

You can also just copy and paste .vscode/launch.json and .vscode/tasks.json from this repository into your own project that uses GoDotTest.

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Play in Editor",
      "preLaunchTask": "build",
      "type": "godot-mono",
      "mode": "playInEditor",
      "request": "launch"
    },
    {
      "name": "Play Scene in Editor",
      "preLaunchTask": "build",
      "type": "godot-mono",
      "mode": "executable",
      "request": "launch",
      "executable": "${env:GODOT}",
      "executableArguments": [
        "${fileDirname}/${fileBasenameNoExtension}.tscn"
      ]
    },
    // We tell the game to run tests by using command line arguments.
    // This means we can't use the "play in editor" option — we have to launch
    // our own instance of Godot.
    //
    // Since passing scene files to Godot doesn't seem to work easily with the
    // C# Tools for Godot VSCode plugin, we use the path to Godot from the
    // environment. Make sure you set the GODOT variable to your Godot
    // executable.
    //
    // On mac, you can add the following to your zsh rc file:
    // alias godot="/Applications/Godot.app/Contents/MacOS/Godot"
    {
      "name": "Debug Tests",
      "type": "godot-mono",
      "mode": "executable",
      "request": "launch",
      "executable": "${env:GODOT}",
      "executableArguments": [
        "--run-tests",
        "--quit-on-finish"
      ],
      "preLaunchTask": "build"
    },
    // Debug the current test!
    //
    // The test runner will look for the class with the same name as the test
    // file that's currently open (disregarding its folder and file extension).
    // The search is case-insensitive.
    {
      "name": "Debug Current Test",
      "type": "godot-mono",
      "mode": "executable",
      "request": "launch",
      "executable": "${env:GODOT}",
      "executableArguments": [
        "--run-tests=${fileBasenameNoExtension}",
        "--quit-on-finish"
      ],
      "preLaunchTask": "build"
    },
    {
      "name": "Launch",
      "type": "godot-mono",
      "request": "launch",
      "mode": "executable",
      "preLaunchTask": "build",
      "executable": "/Applications/Godot.app/Contents/MacOS/Godot",
      "executableArguments": [
        "--path",
        "${workspaceRoot}"
      ]
    },
    {
      "name": "Launch (Select Scene)",
      "type": "godot-mono",
      "request": "launch",
      "mode": "executable",
      "preLaunchTask": "build",
      "executable": "/Applications/Godot.app/Contents/MacOS/Godot",
      "executableArguments": [
        "--path",
        "${workspaceRoot}",
        "${command:SelectLaunchScene}"
      ]
    },
    {
      "name": "Attach",
      "type": "godot-mono",
      "request": "attach",
      "address": "localhost",
      "port": 23685
    }
  ]
}

Note: You will also need the accompanying tasks.json (below) to be able to build the game before running the debug configurations for testing.

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "dotnet",
      "type": "process",
      "args": [
        "build",
        "--no-restore"
      ],
      "problemMatcher": "$msCompile",
      "presentation": {
        "echo": true,
        "reveal": "silent",
        "focus": false,
        "panel": "shared",
        "showReuseMessage": true,
        "clear": false
      }
    }
  ]
}

Godot 4.x Launch Configurations

You can reuse the same tasks.json as shown above for 3.x, but you need a different launch.json file. Be sure to define the GODOT4 environment variable on your system to point to the Godot 4 executable.

{
  "version": "0.2.0",
  "configurations": [
    // For these launch configurations to work, you need to setup a GODOT
    // environment variable. On mac or linux, this can be done by adding
    // the following to your .zshrc, .bashrc, or .bash_profile file:
    // export GODOT="/Applications/Godot.app/Contents/MacOS/Godot"
    {
      "name": "Play",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${env:GODOT4}",
      "args": [],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
    },
    {
      "name": "Debug Tests",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${env:GODOT4}",
      "args": [
        // These command line flags are used by GoDotTest to run tests.
        "--run-tests",
        "--quit-on-finish"
      ],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
    },
    {
      "name": "Debug Current Test",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${env:GODOT4}",
      "args": [
        // These command line flags are used by GoDotTest to run tests.
        "--run-tests=${fileBasenameNoExtension}",
        "--quit-on-finish"
      ],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
    },
  ]
}

Testing a Scene

Create a test folder in your project and create a test scene in it. Add a C# script to the root of the test scene with the following contents:

using System.Reflection;
using Godot;
using GoDotTest;

public class Tests : Node2D {
  public override async void _Ready()
    => await GoTest.RunTests(Assembly.GetExecutingAssembly(), this);
}

Main Scene

In your main scene, you need to determine if tests should be run. GoDotTest relies on the presence of certain command line arguments to determine if tests should be run.

In your main scene, you should construct a test environment from the command line arguments and determine if tests should be run. If they are, you can switch to the test scene. Otherwise, you can switch to the game scene. If you've written your own scene switching system, you can adapt this file to use that accordingly.

using Godot;
using GoDotTest;

public class Main : Node2D {
  public override void _Ready() {
    var testEnv = TestEnvironment.From(OS.GetCmdlineArgs());
    if (testEnv.ShouldRunTests) {
      GetTree().ChangeScene("res://test/Tests.tscn");
    }
    else {
      GetTree().ChangeScene("res://scenes/Game.tscn");
    }
  }
}

Logging

Make sure you add this to your project.godot file so you can see test logs when they're running.

[network]

; Required to see all the logs when tests are running!

limits/debugger_stdout/max_chars_per_second=200000
limits/debugger_stdout/max_messages_per_frame=500
limits/debugger_stdout/max_errors_per_second=500
limits/debugger_stdout/max_warnings_per_second=500

Assertions and Mocking

GoDotTest is only a test provider and test execution system. Keeping the scope of GoDotTest small allows us to update it rapidly and ensure it's always working well with the latest Godot versions.

For mocking, we recommend Moq for Godot 3.x and LightMock.Generator for Godot 4.x (since Moq won't work in Godot 4 until the collectible assemblies support is merged). If you want LightMock's API to more closely resemble Moq's, you can also use Chickensoft's LightMoq adapter.

For integration tests, we recommend GodotTestDriver. GodotTestDriver allows you to create drivers that allow you to simulate input, wait for the next frame, interact with UI elements, create custom test drivers, etc.

Coverage

If your code is configured correctly to switch to the test scene when --run-tests is passed in (see above), you can run all of your tests and generate code coverage while Godot is running.

test coverage

First, install coverlet and reportgenerator.

dotnet tool install --global dotnet-reportgenerator-globaltool
dotnet tool install --global coverlet.console
# Do this too if you're on an M1 mac / ARMx64 system:
# Works around https://github.com/dotnet/efcore/issues/27787#issuecomment-1110061226
dotnet tool update --global coverlet.console

To run Godot with code coverage enabled, use a script like the following (or reference the local coverage.sh.

Note: On macOS, you may need to run chmod +x ./coverage.sh to add execution permissions before you are able to run the coverage.sh script.

Code Coverage Bash Script for Godot 3.x

coverlet .mono/temp/bin/Debug/ --target $GODOT --targetargs \
  "--run-tests --quit-on-finish" --format "lcov" \
  --output ./coverage/coverage.info \
  --exclude-by-file "**/test/**/*.cs" # Don't collect coverage for the tests

reportgenerator \
  -reports:"./coverage/coverage.info" \
  -targetdir:"./coverage/report" \
  -reporttypes:Html

# Open the coverage report in your browser.
open coverage/report/index.html

Code Coverage Bash Script for Godot 4.x

You'll need the latest version of coverlet (> 3.2.0) that hasn't been released yet. You can build coverlet from source by installing .NET 5 SDK and following their contribution guidelines.

You also need to pass the --coverage flag to Godot for GoDotTest to exit correctly. Godot 4's exit behavior doesn't play nicely with coverlet, so GoDotTest needs to know that it should force exit the process via the .NET API's instead of routing the exit request through Godot. This does cause a few error messages to appear as the process exits, but it does not cause any other problems.

# This requires a GODOT4 environment variable.

# Be sure to replace the PATH/TO/coverlet.console/... with the path to
# your newly built version of coverlet below.

dotnet PATH/TO/coverlet.console/bin/Debug/net5.0/coverlet.console.dll "./.godot/mono/temp/bin/Debug" --verbosity detailed \
  --target $GODOT4 \
  --targetargs "--run-tests --coverage --quit-on-finish" \
  --format "opencover" \
  --output "./coverage/coverage.xml" \
  --exclude-by-file "**/scenes/**/*.cs" \
  --exclude-by-file "**/test/**/*.cs" \
  --exclude-by-file "**/*Microsoft.NET.Test.Sdk.Program.cs" \
  --exclude-assemblies-without-sources "missingall"

reportgenerator \
  -reports:"./coverage/coverage.xml" \
  -targetdir:"./coverage/report" \
  -reporttypes:Html

How It Works

GoDotTest uses C# Reflection to find all classes in the current assembly that extend the TestClass it provides. It uses a TestProvider to find and load test suites (classes that extend TestClass) that can be run. It references a TestEnvironment that is created from the command line arguments given to the game/Godot and filters the test suites based on the presence of a test suite name, if given.

GoDotTest uses a TestExecutor to run methods in the order they are declared in a TestClass. Test methods are denoted with the [Test] attribute.

Test output is displayed by a TestReporter which responds to test events.

Auxiliary methods, such as Setup and Cleanup are run before and after each test, respectively. They can be specified with the [Setup] and [Cleanup] attributes on methods in a TestClass.

Additionally, any methods tagged with the [SetupAll] or [CleanupAll] attributes will be run once at the start of the test suite and once at the end, respectively.

GoDotTest will await any async Task test methods it encounters. Tests do not run in parallel, nor are there any plans to add that functionality. The focus of GoDotTest is to provide a simple, C#-first approach to testing in Godot that runs tests in a very simple and deterministic manner.

If you need to customize how tests are loaded and run, you can use the code in GoTest.cs as a starting point.

Command Line Arguments

  • --run-tests: The presence of this flag informs your game that tests should be run. If you've setup your main scene to redirect to the test scene when it finds this flag (as described above), you can use pass this flag in when running Godot from the command line (for debugging or CI/CD purposes) to run your test(s).
  • --quit-on-finish: The presence of this flag indicates that the test runner should exit the application as soon as it is finished running tests.
  • --stop-on-error: The presence of this flag indicates that the test runner should stop running tests when it encounters the first error in any test suite. Without this flag, it will attempt to run all of the test suites.
  • --sequential: The presence of this flag indicates that subsequent test methods in a test suite should be skipped if an error occurs in a test suite method. Use this if your test methods rely on the previous test method completing successfully. This flag is ignored when using --stop-on-error.
  • --coverage: Required when running tests with the intent to collect coverage in Godot 4. Allows GoDotTest to force-exit so that coverlet picks up on the coverage correctly.

For more information about command line flags, see TestEnvironment.cs.

Contributing

For information on contributing, see CONTRIBUTING.md.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Chickensoft.GoDotTest:

Package Downloads
RBG_GodotTools_AutoTest

RBG_GodotTools_AutoTest description.

GitHub repositories (3)

Showing the top 3 popular GitHub repositories that depend on Chickensoft.GoDotTest:

Repository Stars
chickensoft-games/GameDemo
The Chickensoft Game Demo — a fully tested, third-person 3D game built with Godot and C#. Now with saving and loading!
chickensoft-games/GodotEnv
Manage Godot versions and addons from the command line on Windows, macOS, and Linux.
chickensoft-games/AutoInject
Node-based dependency injection for C# Godot scripts at build-time, including utilities for automatic node-binding, additional lifecycle hooks, and .net-inspired notification callbacks.
Version Downloads Last updated
1.5.10 4,534 8/15/2024
1.5.9-godot4.3.0-rc.3 171 8/8/2024
1.5.8-godot4.3.0-rc.2 73 8/1/2024
1.5.7-godot4.3.0-rc.1 134 7/25/2024
1.5.6-godot4.3.0-beta.3 165 7/9/2024
1.5.5-godot4.3.0-beta.2 124 6/20/2024
1.5.4-godot4.3.0-beta.1 192 5/31/2024
1.5.3 1,487 5/15/2024
1.5.2 8,641 4/17/2024
1.5.1-godot4.2.2-rc.2 166 3/13/2024
1.5.0-godot4.2.2-rc.1 207 2/1/2024
1.4.1-godot4.2.2-rc.1 80 1/26/2024
1.4.0 3,883 12/18/2023
1.3.6 626 12/12/2023
1.3.5-godot4.2.1-rc.1 137 12/7/2023
1.3.4 1,112 11/30/2023
1.3.3-godot4.2.0-rc.2 112 11/25/2023
1.3.2-godot4.2.0-beta.5 600 11/7/2023
1.3.1-godot4.2.0-beta.3 100 10/27/2023
1.3.0-godot4.2.0-beta.2 97 10/20/2023
1.2.4-godot4.2.0-beta.2 81 10/19/2023
1.2.3 3,256 10/4/2023
1.2.2-godot4.1.2-rc.1 137 9/22/2023
1.2.1 506 9/4/2023
1.2.0 169 8/29/2023
1.1.19 235 8/21/2023
1.1.18 9,396 7/21/2023
1.1.17 1,296 7/7/2023
1.1.16-godot4.1.0-rc.3 145 7/4/2023
1.1.15-godot4.1.0-rc.2 145 6/30/2023
1.1.14-godot4.1.0-rc.1 130 6/27/2023
1.1.13-godot4.1.0-beta.3 119 6/22/2023
1.1.12-godot4.1.0-beta.2 119 6/14/2023
1.1.11-godot4.1.0-beta.1 116 6/7/2023
1.1.10 317 5/19/2023
1.1.9-godot4.0.3-rc.2 112 5/17/2023
1.1.8-godot4.0.3-rc.1 120 4/27/2023
1.1.7 601 4/4/2023
1.1.6-godot4.0.2-rc.1 805 4/3/2023
1.1.5 203 4/3/2023
1.1.4 1,182 4/3/2023
1.1.2-godot.4.beta.16 2,315 1/30/2023
1.1.2-beta8 183 12/17/2022
1.1.2-beta6 100 11/25/2022
1.1.2-beta.4.0.0.17 702 2/6/2023
1.1.2-beta.4.0.0.16 170 2/6/2023
1.1.1-beta6 96 11/25/2022
1.1.0-beta6 95 11/24/2022
1.0.0 507 9/17/2022
0.0.4 483 6/19/2022
0.0.3 402 6/19/2022
0.0.2 393 6/19/2022
0.0.1 438 6/19/2022

GoDotTest release.