Setting up a visual test project

This page describes how to set up a visual test project. This allows you to easily visually inspect scenes and run automated tests against them. Because osu-framework tests support dynamic recompilation (you can edit your code when the test browser is running), this is the recommended way to develop UI components.

Related osu-framework docs:

Setting up the test browser

If you created your project from an osu-framework template, you likely already have a test project and can skip this step.

Create a new C# project targeting .NET Core 3.0 or higher, set the output type to "Windows Application" (<OutputType>WinExe</OutputType> in your csproj file) and add a NuGet reference to ppy.osu.Framework.

Next, create a new class that inherits from osu.Framework.Game. Create a BackgroundDependencyLoader method and add a TestBrowser to your draw hierarchy, passing in the namespace which all your tests start with. For example:

namespace MyGame.Tests
{
	public class MyGameTestBrowser : Game
	{
		[BackgroundDependencyLoader]
		private void Load()
		{
			Child = new TestBrowser(nameof(MyGame));
		}
	}
}

Often, a DrawSizePreservingFillContainer is used to automatically scale the TestBrowser, so it looks the same on all resolutions. A SafeAreaContainer can also be used, so mobile devices with display notches don't cover anything important:

namespace MyGame.Tests
{
	public class MyGameTestBrowser : Game
	{
		[BackgroundDependencyLoader]
		private void Load()
		{
			Child = new SafeAreaContainer
			{
				RelativeSizeAxes = Axes.Both,
				Child = new DrawSizePreservingFillContainer
				{
					Child = new TestBrowser(nameof(MyGame)),
				},
			};
		}
	}
}

Finally, make sure to launch the game in a GameHost.

namespace MyGame.Tests
{
	internal static class Program
	{
		private static void Main(string[] args)
		{
			using var host = Host.GetSuitableHost("my-game-tests");
			host.Run(new MyGameTestBrowser());
		}
	}
}

If you launch your test project now, it should look like this:

An empty visual test browser

Creating visual tests

To create a visual test, simple create a class that inherits from osu.Framework.Testing.TestScene. This is a container, so you can create a BackgroundDependencyLoader method and add children to the draw hierarchy.

You can also add "steps" to the test scene. Steps are actions that interact with the test scene, usually to set up different test configurations or to check if certain conditions are true.

This is an example of a test which uses different kinds of steps:

		[BackgroundDependencyLoader]
		private void Load()
		{
			// Create a drawable and add it to the draw hierarchy
			var box = new Box
			{
				Size = new Vector2(200),

				Anchor = Anchor.Centre,
				Origin = Anchor.Centre,
			};
			Add(box);

			// AddStep creates a step that just executes some code. It only fails if an exception is thrown.
			AddStep("Change color to red", () => box.Colour = Color4.Tomato);
			AddStep("Change color to blue", () => box.Colour = Color4.RoyalBlue);

			// AddAssert adds a step that checks for a condition, and fails if it is false.
			AddAssert("Is box blue?", () => box.Colour == Color4.RoyalBlue);

			// AddRepeatStep runs some code multiple times
			AddRepeatStep("Add 15° to rotation", () => box.Rotation += 15f, 6);
			AddAssert("Is box rotation 90°?", () => Precision.AlmostEquals(box.Rotation, 90f));

			// AddWaitStep simply waits before executing the next step
			// The waitCount parameter is the amount of repeats, so this is equivalent to a repeat step with no action
			AddWaitStep("Wait 5 steps", 5);

			// AddUntilStep waits until a condition is true, or fails when a timeout has passed
			AddStep("Rotate to 360° over 3s", () => box.RotateTo(360f, 3_000.0));
			AddUntilStep("Box has rotated to 360°", () => Precision.AlmostBigger(box.Rotation, 360f));

			// AddToggleStep adds an action with a boolean parameter
			AddToggleStep("Shear box", b => box.Shear = b ? new Vector2(0.5f, 0f) : Vector2.Zero);

			// AddSliderStep adds an action with a slider that the user can change
			AddSliderStep("Change scale", 0.5f, 3f, 1f, val => box.Scale = new Vector2(val));
		}

Customizing the TestScene

The osu! project likes to create its own TestScene classes. This allows you to set up dependency injection, add resources, create specialized test runners or provide common functionality. An example can be found here from the osu! repository at 2021/07/05.


Last verified against v2021.707.0.