Manifest Setup and Dependencies
To use GameTests, your behavior pack manifest must declare a script module entry and a dependency on `@minecraft/server-gametest`. Unlike the core `@minecraft/server` module, `@minecraft/server-gametest` has remained on beta versions and requires the Beta APIs experimental toggle to be enabled in the world settings.
Almost every GameTest script also imports classes or types from `@minecraft/server` to interact with the entities and blocks spawned inside the test boundary. Therefore, you should list both dependencies in your `manifest.json` file.
{
"format_version": 2,
"header": {
"name": "GameTest Suite",
"description": "Automated tests for custom mechanics",
"uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
"version": [1, 0, 0],
"min_engine_version": [1, 20, 80]
},
"modules": [
{
"type": "script",
"language": "javascript",
"uuid": "f1e2d3c4-b5a6-4958-8796-a5b4c3d2e1f0",
"entry": "scripts/main.js",
"version": [1, 0, 0]
}
],
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "1.11.0"
},
{
"module_name": "@minecraft/server-gametest",
"version": "1.0.0-beta"
}
]
}Registering a Basic Test and Structure
A GameTest is registered at script load time by calling `GameTest.register(className, testName, callback)`. The `className` and `testName` strings combine to form the full test identifier, formatted as `className:testName`, which you pass to the `/gametest run` command in-game. The callback function receives a `Test` helper object that controls the lifecycle of that specific test instance.
Every GameTest requires a `.mcstructure` file to define its physical bounds and initial block layout. By default, the engine looks for a structure in the `BP/structures/<className>/<testName>.mcstructure` path, though you can override this using the `.structureName()` method on the registration builder. Coordinates passed to methods on the `Test` object are always relative to the northwest bottom corner of the loaded structure, making your tests completely independent of where they are spawned in the world.
import * as GameTest from "@minecraft/server-gametest";
import { MinecraftBlockTypes } from "@minecraft/server";
GameTest.register("custom_mechanics", "creeper_trap", (test) => {
const spawnPos = { x: 2, y: 2, z: 2 };
const creeper = test.spawn("minecraft:creeper", spawnPos);
test.succeedWhen(() => {
test.assertEntityPresentInArea("minecraft:item", true);
test.assertEntityInstancePresent(creeper, false);
});
})
.structureName("custom_mechanics:trap_room")
.maxTicks(200)
.tag(GameTest.Tags.suiteDefault);Registration Builder Methods
When you call `GameTest.register()`, it returns a `RegistrationBuilder` object. You can chain methods onto this return value to configure how long the test is allowed to run, what structure it loads, and how it is grouped.
| Method | What it does | Notes |
|---|---|---|
| structureName(name) | Sets the structure to load from BP/structures/ | Format is folder:filename without the .mcstructure extension. |
| maxTicks(ticks) | Sets the timeout limit in game ticks (20 ticks = 1 second) | Defaults to 100 ticks if omitted. Test fails if time runs out. |
| setupTicks(ticks) | Wait time after loading structure before callback runs | Useful for letting water flow or sand fall before logic starts. |
| tag(tagName) | Assigns a category tag for batch running | Allows running groups via /gametest runset <tag>. |
| required(isRequired) | Marks whether failure fails the entire test batch | Defaults to true. Set false for flaky or optional tests. |
The Test Helper and Assertions
Inside your test callback, the `test` argument provides two main categories of methods: actions and assertions. Action methods manipulate the area inside the structure, such as `test.spawn()`, `test.setBlockType()`, `test.pressButton()`, and `test.pullLever()`. Because all coordinates passed to these methods are relative to the structure origin, you never need to calculate world offsets yourself. If you need to pass a location to a standard `@minecraft/server` method, you can convert between coordinate spaces using `test.worldLocation(relativePos)` and `test.relativeLocation(worldPos)`.
A test does not pass simply because its callback finishes executing. You must explicitly resolve the test by calling `test.succeed()`, `test.fail(errorMessage)`, or by setting up a polling condition like `test.succeedWhen(callback)`. Inside `succeedWhen`, you call assertion methods such as `test.assertBlockPresent()`, `test.assertEntityPresent()`, or `test.assert(condition, message)`. The engine runs the `succeedWhen` callback every tick; if an assertion throws an error, the engine catches it and waits for the next tick. If all assertions inside `succeedWhen` run without throwing before `maxTicks` expires, the test immediately succeeds and the structure beacon turns green.
Spawning and Controlling Simulated Players
One of the most powerful features of `@minecraft/server-gametest` is the `SimulatedPlayer` class. By calling `test.spawnSimulatedPlayer(relativeLocation, name, gameMode)`, you spawn a fake player entity that loads chunks, triggers player-only sensors, and can perform complex interactions like attacking, using items, breaking blocks, and navigating paths.
Developers often use a dummy GameTest with a massive `maxTicks` value and an empty 1x1x1 structure just to spawn a `SimulatedPlayer` for general gameplay automation or stress testing. Note that a `SimulatedPlayer` is automatically despawned when the GameTest that created it finishes or is cleared via `/gametest clearall`.
import * as GameTest from "@minecraft/server-gametest";
import { GameMode } from "@minecraft/server";
GameTest.register("bot_util", "miner_bot", (test) => {
const bot = test.spawnSimulatedPlayer({ x: 1, y: 2, z: 1 }, "MinerBot", GameMode.survival);
test.runAfterDelay(20, () => {
bot.lookAtBlock({ x: 1, y: 2, z: 2 });
bot.breakBlock({ x: 1, y: 2, z: 2 });
});
test.succeedWhen(() => {
test.assertBlockPresent("minecraft:air", { x: 1, y: 2, z: 2 }, true);
});
})
.structureName("bot_util:stone_room")
.maxTicks(400);Running Tests In-Game
Once your behavior pack is active in a world with Beta APIs enabled, you interact with your tests using the `/gametest` slash command. Running `/gametest run <className>:<testName>` spawns the structure in front of you, places a status beacon block at the corner, and executes the script.
During active development, `/gametest runthis` will re-run the nearest test structure in the world, and `/gametest clearall` removes all spawned test structures and simulated players within range. You can also create a new `.mcstructure` directly in-game by running `/gametest create <name> <width> <height> <length>`, building your test setup inside the generated frame, and exporting the structure using the structure block UI into your behavior pack's `structures` folder.
Which versions this applies to
Available since Bedrock Edition 1.16.210. Because `@minecraft/server-gametest` only exists as a `1.0.0-beta` version module, it strictly requires the 'Beta APIs' (or 'Enable GameTest Framework' in older versions) experimental toggle to be turned on in the world settings.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log error: Module [@minecraft/server-gametest] with version [1.0.0] was not found. | You declared version "1.0.0" in your manifest.json dependencies, but @minecraft/server-gametest has not had a stable release and only exists under the -beta tag, or the Beta APIs toggle is turned off on the world. | Change the version in manifest.json dependencies to "1.0.0-beta" and enable the Beta APIs experimental toggle in the world settings. |
| Running /gametest run fails immediately with an error stating it could not load or find the structure. | The engine could not find a matching .mcstructure file in your behavior pack's structures sub-directory. | Ensure you have a valid .mcstructure file placed at BP/structures/<folder>/<name>.mcstructure, and pass "<folder>:<name>" to the .structureName() builder method. |
| The test finishes all code inside the callback, the structure sits idle for a few seconds, and then the beacon turns red with a timeout error. | A GameTest does not automatically pass when the callback function returns; it waits until maxTicks is reached unless told otherwise. | Call test.succeed() at the end of your logic, or wrap your completion checks inside test.succeedWhen(() => { ... }). |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
