Game tests
In-game tests create a real world on Mojang's game test server, run the mod code, and check the result. No window opens and no EULA agreement is needed, so they are good to run after every change or in CI.
Create a new file, GardenTests.java.
src/main/java/com/example/crystalgarden/GardenTests.java
final class GardenTests {
private static final BlockPos ABOVE_FLOOR = BlockPos.of(0, 1, 0);
private GardenTests() {}
static void register(GameTestRegistrar tests) {
// A sprout reaches stage 1 after TICKS_PER_STAGE ticks on its own.
tests.register("sprout_grows", CrystalSprout.TICKS_PER_STAGE + 40, test -> {
test.setBlock(ABOVE_FLOOR, CrystalGardenMod.CRYSTAL_SPROUT);
BlockPos at = test.absolute(ABOVE_FLOOR);
test.runAfter(CrystalSprout.TICKS_PER_STAGE + 5, () -> {
int stage = test.world().stateAt(at).get(CrystalSprout.STAGE);
test.assertTrue(stage == 1, "stage after " + CrystalSprout.TICKS_PER_STAGE + " ticks is " + stage);
test.succeed();
});
});
// A ripe sprout gives crystals, starts again, and the harvest is counted for the player and the world.
tests.register("ripe_sprout_can_be_harvested", test -> {
test.setBlock(ABOVE_FLOOR, CrystalGardenMod.CRYSTAL_SPROUT);
BlockPos at = test.absolute(ABOVE_FLOOR);
test.assertTrue(CrystalSprout.ripen(test.world(), at), "the sprout ripened");
int harvestsBefore = test.world()
.storage(CrystalGardenMod.GARDEN_STATS, CrystalGardenMod.GARDEN_STATS_OPTIONS)
.data()
.getInt("harvests", 0);
PlayerView player = test.createMockServerPlayer();
test.useBlock(ABOVE_FLOOR, player);
test.assertTrue(
player.countItem(CrystalGardenMod.CRYSTAL) == CrystalSprout.CRYSTALS_PER_HARVEST,
"the player holds " + player.countItem(CrystalGardenMod.CRYSTAL) + " crystal(s)");
test.assertTrue(test.world().stateAt(at).get(CrystalSprout.STAGE) == 0, "the sprout starts again");
int counted = player.data(CrystalGardenMod.harvested).data().getInt("count", 0);
test.assertTrue(counted == CrystalSprout.CRYSTALS_PER_HARVEST, "the player's count is " + counted);
int harvests = test.world()
.storage(CrystalGardenMod.GARDEN_STATS, CrystalGardenMod.GARDEN_STATS_OPTIONS)
.data()
.getInt("harvests", 0);
test.assertTrue(harvests == harvestsBefore + 1, "the world counted " + harvests + " harvest(s)");
test.succeed();
});
// The server may use /garden grow; a fresh player may not.
tests.register("grow_command_needs_permission", test -> {
test.setBlock(ABOVE_FLOOR, CrystalGardenMod.CRYSTAL_SPROUT);
int result = test.runCommand("garden grow ~ ~1 ~");
test.assertTrue(result == 1, "/garden grow returned " + result);
int stage = test.world().stateAt(test.absolute(ABOVE_FLOOR)).get(CrystalSprout.STAGE);
test.assertTrue(stage == CrystalSprout.RIPE, "stage after /garden grow is " + stage);
PlayerView player = test.createMockServerPlayer();
test.assertTrue(
!player.hasPermission(CrystalGardenMod.growPermission), "a new player may not ripen sprouts");
test.succeed();
});
// The crystal block drops itself (the loot table polyResources generates) and is mined with a pickaxe.
tests.register("crystal_block_resources", test -> {
test.setBlock(ABOVE_FLOOR, CrystalGardenMod.CRYSTAL_BLOCK);
int matches = test.runCommand("execute if block ~ ~1 ~ #minecraft:mineable/pickaxe");
test.assertTrue(matches == 1, "the crystal block is in #minecraft:mineable/pickaxe");
int dropped = test.runCommand("loot spawn ~ ~2 ~ loot crystal_garden:blocks/crystal_block");
test.assertTrue(dropped == 1, "the loot table dropped " + dropped + " stack(s)");
test.succeed();
});
}
}
Register them on the last line of the entrypoint.
CrystalGardenMod.java (end of onInitializeCommon)
GardenTests.register(mod.gameTests());
- Each test gets a small test area. Positions are relative to the area (
BlockPos.of(0, 1, 0)is right above the floor), andtest.absolute(...)gives the world position. - The default time limit is 100 ticks.
sprout_growsmust wait 105 ticks for the sprout to grow one stage, so it getsTICKS_PER_STAGE + 40ticks. test.runCommand(...)runs with server permission and returns the command's result value.createMockServerPlayer()is a fake player that has not joined. It is not an operator, so it does not have thecrystal_garden:growpermission.
Run
- Windows (PowerShell)
- macOS · Linux (bash)
.\gradlew.bat gameTests "-PpolyTests=crystal_garden:*"
./gradlew gameTests "-PpolyTests=crystal_garden:*"
If everything passes, it ends like this.
[GameTestServer] Each test that matches crystal_garden:* will be run 1 times (total: 4)
[GameTestServer] All 4 required tests passed :)
If a test fails, it tells you which check in which test was wrong (a message like the player holds 0 crystal(s)). The full test API is in In-game tests.
Next step
In Build, run, and distribute, you run the mod in game and distribute the jar.