| name | Minecraft GameTest Framework |
| description | This skill should be used when the user wants to create, run, or manage Minecraft GameTest Framework tests for Bedrock Edition behavior packs. Covers project setup, test registration, structure creation, assertion API, simulated players, async tests, and test organization best practices. |
| version | 1.0.0 |
Minecraft GameTest Framework
You are an expert in the Minecraft GameTest Framework for Bedrock Edition. You write clear, minimal tests that validate specific behaviors — not integration marathons.
Documentation References
Always fetch fresh docs before writing test code — the API is versioned and beta APIs evolve:
- Getting started guide:
https://learn.microsoft.com/en-ca/minecraft/creator/documents/gametestgettingstarted
- Building your first GameTest:
https://learn.microsoft.com/en-ca/minecraft/creator/documents/gametestbuildyourfirstgametest
@minecraft/server-gametest API reference: https://learn.microsoft.com/en-ca/minecraft/creator/scriptapi/minecraft/server-gametest/minecraft-server-gametest
- Scripting setup (TypeScript):
https://learn.microsoft.com/en-ca/minecraft/creator/documents/scriptinggettingstarted
- Official example tests:
https://github.com/microsoft/minecraft-gametests
- Scripting samples (TypeScript starter):
https://github.com/microsoft/minecraft-scripting-samples
See references/gametest-links.md for a curated link list.
Project Setup
Prerequisites
- Node.js LTS (
https://nodejs.org/)
- Visual Studio Code
- Minecraft Bedrock Edition with Beta APIs experiment enabled
- World settings: Creative mode, Flat world, Normal difficulty, Cheats enabled
Behavior Pack Structure
my-tests/
├── manifest.json
├── structures/
│ └── my-tests/
│ └── my_structure.mcstructure # exported from in-game structure block
└── scripts/
└── MyTests.js # or MyTests.ts with build step
Deploy to:
%APPDATA%\Roaming\Minecraft\development_behavior_packs\my-tests\
manifest.json
{
"format_version": 2,
"header": {
"description": "My GameTests",
"name": "My Tests",
"uuid": "<generate-unique-uuid>",
"version": [1, 0, 0],
"min_engine_version": [1, 21, 0]
},
"modules": [
{
"description": "GameTest scripts",
"type": "script",
"language": "javascript",
"uuid":
Always use separate UUIDs for header and each modules entry. Generate via uuidgen or an online tool.
TypeScript Setup (preferred)
npm install
Configure .env:
PROJECT_NAME="my-tests"
MINECRAFT_PRODUCT="BedrockGDK"
Build commands:
npx just-scripts local-deploy
npx just-scripts local-deploy --watch
npx just-scripts lint
npx just-scripts lint --fix
npx just-scripts mcaddon
Registering Tests
Basic synchronous test
import { register } from '@minecraft/server-gametest';
register('MyTestClass', 'myTestName', (test) => {
test.succeed();
})
.maxTicks(200)
.structureName('my-tests:my_structure');
- First arg: class name — groups related tests, used in
/gametest run commands
- Second arg: test name — unique within the class
- Third arg: test function receiving a
Test object
Async test
import { registerAsync } from '@minecraft/server-gametest';
registerAsync('MyTestClass', 'asyncTest', async (test) => {
test.spawn('minecraft:creeper', { x: 2, y: 2, z: 2 });
await test.idle(40);
test.assertEntityPresentInArea('minecraft:creeper', true);
test.succeed();
})
.maxTicks(300)
.structureName('my-tests:my_structure');
RegistrationBuilder chaining
register('MyClass', 'myTest', fn)
.maxTicks(400)
.structureName('ns:name')
.rotateTest(true)
.batch('myBatch')
.required(true)
.tag('myTag');
Test API — Core Methods
Spawning
test.spawn('minecraft:fox', { x: 5, y: 2, z: 5 });
test.spawnItem(new ItemStack(MinecraftItemTypes.Apple), pos);
Assertions
test.assertEntityPresentInArea('minecraft:chicken', true);
test.assertEntityPresentInArea('minecraft:chicken', false);
test.assertBlockPresent(MinecraftBlockTypes.Stone, pos, true);
test.assertEntityHasComponent(entity, 'minecraft:health');
test.assertRedstonePower(pos, 15);
test.assertIsWaterlogged(pos, true);
Completion
test.succeed();
test.fail('reason string');
test.succeedWhen(() => {
test.assertEntityPresentInArea('minecraft:chicken', false);
});
test.succeedOnTick(100);
test.succeedWhenEntityPresent('minecraft:chicken', pos, false);
test.succeedWhenBlockPresent(MinecraftBlockTypes.Stone, pos, true);
Utilities
test.idle(20);
test.walkTo(player, pos, 1);
test.print('debug message');
Simulated Player
const player = test.spawnSimulatedPlayer({ x: 2, y: 2, z: 2 }, 'TestPlayer');
player.lookAtEntity(entity);
player.attack();
player.jumpUp();
player.moveToLocation({ x: 5, y: 2, z: 5 }, { strafingSpeed: 1 });
player.useItemInSlot(0);
player.interact();
Creating Structures
Every test needs a .mcstructure file that defines the environment:
- Build your test environment in-game in Creative mode
- Run
/give @s structure_block
- Place the structure block adjacent to your build
- Open its UI → set mode to Save
- Name it (e.g.,
my-tests:my_structure) — namespace must match your pack id
- Set bounds to encompass the build
- Click Save
- Copy the exported
.mcstructure from:
%APPDATA%\Roaming\Minecraft\games\com.mojang\structures\
- Place in
structures/my-tests/my_structure.mcstructure
Keep structures minimal. Test environments should be the smallest space that exercises the behavior. Oversized structures slow test runs and obscure failures.
Running Tests
| Command | Effect |
|---|
/gametest run <class>:<name> | Run one test |
/gametest runset <class> | Run all tests in a class |
/gametest runall | Run every registered test |
/gametest runthis | Run test the player is inside |
/gametest clearall | Remove all active test structures |
/gametest runbatch <batchName> | Run tests tagged with a batch |
Tests spawn structures starting at your position. Use a flat, open area.
Test Organization Guidelines
Naming
- Class names:
PascalCase, descriptive category (CombatTests, RedstoneTests)
- Test names:
camelCase, reads as a sentence (foxAttacksChicken, pistonPushesBlock)
- Structure names:
snake_case, match the test (fox_attacks_chicken)
One behavior per test
Each test validates exactly one thing. If a test needs 10 assertions to pass, split it into multiple tests. A failed test name should immediately communicate what broke.
Tick budgets
| Scenario | Suggested maxTicks |
|---|
| Instant assertion | 20–50 |
| Mob AI interaction | 200–400 |
| Redstone circuit | 40–100 |
| Simulated player sequence | 300–600 |
Set maxTicks to the minimum needed — tests that run long mask performance regressions.
Async vs sync
- Use sync (
register) + succeedWhen for event-driven conditions (mob dies, block changes)
- Use async (
registerAsync) + await test.idle() when you need precise tick control or sequential player actions
Batch grouping
Group related tests with .batch('batchName') so you can run subsets:
/gametest runbatch combatSuite
Before/after batch hooks
import { setBeforeBatchCallback, setAfterBatchCallback } from '@minecraft/server-gametest';
setBeforeBatchCallback('combatSuite', () => {
});
setAfterBatchCallback('combatSuite', () => {
});
Do not
- Do not rely on world state outside the structure — tests run concurrently and can interfere
- Do not hardcode absolute coordinates — use relative positions within the structure volume
- Do not leave
test.print() calls in committed tests — remove debug output before shipping
- Do not use
maxTicks as a timeout hack — if a condition is never met, the test design is wrong
File Organization for Multiple Test Suites
scripts/
├── index.js # imports all test files (entry point)
├── combat/
│ ├── FoxTests.js
│ └── CreeperTests.js
├── redstone/
│ └── PistonTests.js
└── player/
└── InventoryTests.js
index.js:
import './combat/FoxTests.js';
import './combat/CreeperTests.js';
import './redstone/PistonTests.js';
import './player/InventoryTests.js';
Manifest entry points only to scripts/index.js.