[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6D2SFXWZCSB6G1YGEV6FGSX":3,"skill-same:01M6D2SFXWZCSB6G1YGEV6FGSX":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":51,"repositoryHtmlUrl":52,"repositoryHomepageUrl":6,"ownerHtmlUrl":53,"ownerBlogUrl":54,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M6D2SFXWZCSB6G1YGEV6FGSX","minecraft-testing",null,"Design and implement automated tests for current Minecraft 26.x or legacy 1.21.x mods and plugins using JUnit, MockBukkit, NeoForge Game Tests, or Fabric Game Tests. Use for test code and test execution, not release publishing or gameplay implementation.","Jahrome907\u002Fminecraft-agent-skills","Jahrome907","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F222458345?v=4","01M6D2JFFY1EQCC572G6G3X1R2","Joe ","01M6D2JFS2EE87QN8QCFVFY6M8",199,"MIT","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fminecraft-codex-skills\u002Fskills\u002Fminecraft-testing\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"minecraft testing","minecraft","testing","game","tests","Legacy","implementation","publishing","gameplay","neoforge","use","execution","plugins","release","code","fabric","test","implement","design","2026-11-10 10:12:30",false,"---\nname: minecraft-testing\ndescription: \"Design and implement automated tests for current Minecraft 26.x or legacy 1.21.x mods and plugins using JUnit, MockBukkit, NeoForge Game Tests, or Fabric Game Tests. Use for test code and test execution, not release publishing or gameplay implementation.\"\n---\n\n# Minecraft Testing Skill\n\n## Testing Strategies Overview\n\n| Approach | Best For | Requires Game? |\n|----------|---------|----------------|\n| **JUnit Jupiter** (pure unit tests) | Logic, data structures, NBT serialization | No |\n| **MockBukkit** | Bukkit\u002FPaper plugin events, commands, inventory | No (mocked server) |\n| **NeoForge GameTests** | In-game block\u002Fentity\u002Fworld interaction | Yes (test environment) |\n| **Fabric GameTests** | In-game block\u002Fentity\u002Fworld interaction | Yes (test environment) |\n| **Integration server** | Full plugin\u002Fmod lifecycle | Yes (dedicated test server) |\n\nUse Java 25 for current 26.x projects. Keep legacy 1.21.x examples on Java 21\nand Forge 1.20.1 on Java 17. Do not combine source layouts or APIs across versions.\n\n### Routing Boundaries\n- `Use when`: the task is designing or implementing automated tests (unit, mock, gametest, CI test jobs) for Minecraft projects.\n- `Do not use when`: the task is implementing gameplay features rather than testing them (`minecraft-modding`, `minecraft-plugin-dev`, `minecraft-datapack`).\n- `Do not use when`: the task is release automation or publishing pipelines (`minecraft-ci-release`).\n\n## Bundled References And Helpers\n\n- Layout guide: `references\u002Ftest-layouts.md`\n- Fixture\u002Flayout validator: `.\u002Fscripts\u002Fvalidate-test-layout.sh --root \u003Cproject>`\n\nUse the validator before copying a test layout into a real project. It checks\nvisible static dependencies, metadata, and literal structure references. It does\nnot compile the project or prove a Game Test can run.\n\n---\n\n## Unit Testing (JUnit Jupiter — No Minecraft)\n\n### JUnit Platform task\n```kotlin\ntasks.test {\n    useJUnitPlatform()\n    testLogging {\n        events(\"passed\", \"skipped\", \"failed\")\n    }\n}\n```\n\nFor Fabric code that needs loader setup, use Fabric Loader JUnit rather than\nassuming ordinary JUnit initialized Minecraft:\n\n```groovy\ndependencies {\n    testImplementation \"net.fabricmc:fabric-loader-junit:${project.loader_version}\"\n}\n\ntest {\n    useJUnitPlatform()\n}\n```\n\nWhen a unit test reaches registry-dependent Minecraft classes, initialize only\nthe required bootstrap in test setup. The current Fabric guide uses\n`SharedConstants.tryDetectVersion()` and `Bootstrap.bootStrap()` for that case.\n\n### Example pure unit test\n```java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CooldownManagerTest {\n\n    @Test\n    void cooldown_tracksPlayersIndependently() {\n        var manager = new CooldownManager(500L); \u002F\u002F 500ms cooldown\n        manager.startCooldown(\"steve\");\n        assertTrue(manager.isOnCooldown(\"steve\"));\n        assertFalse(manager.isOnCooldown(\"notExisting\"));\n    }\n\n    @Test\n    void cooldown_throwsIllegalArgument_onNegativeDuration() {\n        assertThrows(IllegalArgumentException.class,\n            () -> new CooldownManager(-1L));\n    }\n}\n```\n\n---\n\n## MockBukkit (Paper\u002FBukkit Plugin Tests)\n\nThe paired dependencies below are a retained 26.2 example. For 26.3, verify\na published MockBukkit artifact for that exact Paper line before changing either\ndependency; a 26.2 mock does not establish 26.3 server behavior.\n\n### `build.gradle.kts`\n```kotlin\nrepositories {\n    maven(\"https:\u002F\u002Frepo.papermc.io\u002Frepository\u002Fmaven-public\u002F\")\n    mavenCentral()\n}\n\ndependencies {\n    compileOnly(\"io.papermc.paper:paper-api:26.2.build.+\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter:6.1.3\")\n    testImplementation(\"org.mockbukkit.mockbukkit:mockbukkit-v26.2:4.116.1\")\n    testRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n}\n```\n\n### Setup \u002F teardown pattern\n```java\nimport org.mockbukkit.mockbukkit.MockBukkit;\nimport org.mockbukkit.mockbukkit.ServerMock;\nimport org.mockbukkit.mockbukkit.entity.PlayerMock;\nimport org.junit.jupiter.api.*;\n\nclass MyPluginTest {\n\n    private ServerMock server;\n    private MyPlugin plugin;\n\n    @BeforeEach\n    void setUp() {\n        \u002F\u002F Start mock Bukkit server and load your plugin\n        server = MockBukkit.mock();\n        plugin = MockBukkit.load(MyPlugin.class);\n    }\n\n    @AfterEach\n    void tearDown() {\n        MockBukkit.unmock();\n    }\n}\n```\n\n### Testing events\n```java\n@Test\nvoid playerJoin_getsWelcomeMessage() {\n    PlayerMock player = server.addPlayer(\"Steve\"); \u002F\u002F fires PlayerJoinEvent\n\n    \u002F\u002F Assert the message sent by the plugin's join listener.\n    player.assertSaid(\"Welcome, Steve!\");\n}\n\n@Test\nvoid onBlockBreak_cancelledForNonOp() {\n    PlayerMock player = server.addPlayer();\n    player.setOp(false);\n\n    Block block = player.getWorld().getBlockAt(0, 64, 0);\n    block.setType(Material.STONE);\n    BlockBreakEvent event = new BlockBreakEvent(block, player);\n    server.getPluginManager().callEvent(event);\n\n    assertTrue(event.isCancelled(), \"Non-op should not be able to break blocks\");\n}\n```\n\n### Testing commands\n```java\n@Test\nvoid mypluginInfo_returnsVersion() {\n    PlayerMock player = server.addPlayer(\"Admin\");\n    player.setOp(true);\n\n    boolean result = server.dispatchCommand(player, \"myplugin info\");\n\n    assertTrue(result);\n    player.assertSaid(\"Version: \" + plugin.getDescription().getVersion());\n}\n\n@Test\nvoid mypluginReload_requiresAdminPermission() {\n    PlayerMock player = server.addPlayer(\"NonOp\");\n    player.setOp(false);\n\n    server.dispatchCommand(player, \"myplugin reload\");\n\n    player.assertSaid(\"No permission.\");\n}\n```\n\n### Testing inventory \u002F items\n```java\n@Test\nvoid giveKitCommand_givesPlayerItems() {\n    PlayerMock player = server.addPlayer();\n    \n    server.dispatchCommand(player, \"kit starter\");\n    \n    \u002F\u002F Check inventory\n    assertTrue(player.getInventory().contains(Material.STONE_SWORD));\n    assertTrue(player.getInventory().contains(Material.BREAD, 16));\n}\n```\n\n### Testing scheduler tasks\n```java\n@Test\nvoid repeatingTask_firesAfterDelay() {\n    PlayerMock player = server.addPlayer();\n    \n    \u002F\u002F Execute 40 ticks worth of scheduled tasks\n    server.getScheduler().performTicks(40L);\n    \n    \u002F\u002F Assert expected side effect happened\n    assertEquals(2, plugin.getTaskCount());\n}\n```\n\n### Testing Folia-safe scheduler abstractions\n\nMockBukkit does not emulate Folia's region-threaded runtime. The safe pattern is to\nwrap scheduling behind your own interface and unit test the abstraction boundary.\n\n```java\ninterface SchedulerFacade {\n    void runPlayerTask(Player player, Runnable task);\n    void runAsync(Runnable task);\n}\n\n@Test\nvoid playerTask_delegatesThroughFacade() {\n    List\u003CString> calls = new ArrayList\u003C>();\n    SchedulerFacade facade = new SchedulerFacade() {\n        @Override\n        public void runPlayerTask(Player player, Runnable task) {\n            calls.add(\"player\");\n            task.run();\n        }\n\n        @Override\n        public void runAsync(Runnable task) {\n            calls.add(\"async\");\n            task.run();\n        }\n    };\n\n    facade.runPlayerTask(server.addPlayer(), () -> calls.add(\"ran\"));\n    assertEquals(List.of(\"player\", \"ran\"), calls);\n}\n```\n\n### Testing PDC\n```java\nimport java.util.ArrayList;\nimport org.bukkit.damage.DamageSource;\nimport org.bukkit.damage.DamageType;\nimport org.bukkit.entity.LivingEntity;\n\n@Test\nvoid pdcKillCount_incrementsOnKill() {\n    PlayerMock player = server.addPlayer();\n    NamespacedKey key = new NamespacedKey(plugin, \"kills\");\n\n    \u002F\u002F EntityDeathEvent requires a living victim and an explicit damage source.\n    LivingEntity victim = (LivingEntity) server.addMockEntity(EntityType.ZOMBIE);\n    DamageSource damageSource = DamageSource.builder(DamageType.GENERIC)\n        .withCausingEntity(player)\n        .withDirectEntity(player)\n        .build();\n    EntityDeathEvent deathEvent = new EntityDeathEvent(\n        victim, damageSource, new ArrayList\u003C>(), 0\n    );\n    server.getPluginManager().callEvent(deathEvent);\n\n    int kills = player.getPersistentDataContainer()\n        .getOrDefault(key, PersistentDataType.INTEGER, 0);\n    assertEquals(1, kills);\n}\n```\n\nThis dispatches a synthetic death event. For player attribution, the listener\nunder test should read `event.getDamageSource().getCausingEntity()`; test actual\ncombat attribution separately on a real server.\n\n### Testing item or chunk PDC writes\n```java\n@Test\nvoid itemPdc_roundTripsCustomId() {\n    NamespacedKey key = new NamespacedKey(plugin, \"custom_id\");\n    ItemStack item = new ItemStack(Material.STICK);\n\n    item.editMeta(meta -> meta.getPersistentDataContainer().set(\n        key, PersistentDataType.STRING, \"wand\"\n    ));\n\n    String value = item.getItemMeta().getPersistentDataContainer()\n        .get(key, PersistentDataType.STRING);\n    assertEquals(\"wand\", value);\n}\n```\n\n---\n\n## Current NeoForge Game Tests (26.x)\n\nNeoForge 1.21.5 and later uses data-driven test environments and test instances,\nnot the old `@GameTestHolder` method-registration API. Store resources under\n`data\u002F\u003Cnamespace>\u002Ftest_environment\u002F` and `data\u002F\u003Cnamespace>\u002Ftest_instance\u002F`.\nA `test_instance` selects its environment, structure, timing, and either a\nregistered function or a block-based test.\n\n```json\n{\n  \"environment\": \"minecraft:default\",\n  \"structure\": \"examplemod:example_structure\",\n  \"max_ticks\": 200,\n  \"setup_ticks\": 0,\n  \"required\": true,\n  \"type\": \"minecraft:function\",\n  \"function\": \"examplemod:example_function\"\n}\n```\n\nRegister the `Consumer\u003CGameTestHelper>` with a `DeferredRegister` for the\ncurrent `BuiltInRegistries.TEST_FUNCTION` registry, then attach that register to\nthe mod event bus. The function below makes the JSON reference above usable.\nUse `RegisterGameTestsEvent` only when registering environments and test\ninstances in code instead of data files. Keep the referenced structure in\n`data\u002F\u003Cnamespace>\u002Fstructure\u002F\u003Cpath>.nbt` and mark success explicitly.\n\n```java\nimport java.util.function.Consumer;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\nimport net.neoforged.bus.api.IEventBus;\nimport net.neoforged.fml.common.Mod;\nimport net.neoforged.neoforge.registries.DeferredHolder;\nimport net.neoforged.neoforge.registries.DeferredRegister;\n\n@Mod(ExampleGameTests.MOD_ID)\npublic final class ExampleGameTests {\n    public static final String MOD_ID = \"examplemod\";\n    private static final DeferredRegister\u003CConsumer\u003CGameTestHelper>> TEST_FUNCTIONS =\n        DeferredRegister.create(BuiltInRegistries.TEST_FUNCTION, MOD_ID);\n    public static final DeferredHolder\u003CConsumer\u003CGameTestHelper>, Consumer\u003CGameTestHelper>>\n        EXAMPLE_FUNCTION = TEST_FUNCTIONS.register(\n            \"example_function\", () -> ExampleGameTests::exampleTest\n        );\n\n    public ExampleGameTests(IEventBus modBus) {\n        TEST_FUNCTIONS.register(modBus);\n    }\n\n    public static void exampleTest(GameTestHelper helper) {\n        helper.assertBlockPresent(Blocks.AIR, 0, 0, 0);\n        helper.succeed();\n    }\n}\n```\n\nRun `.\u002Fgradlew runGameTestServer`; the server exits with the count of required\nfailed tests. This command is for a real project, not this skills repository.\n\n---\n\n## Current Fabric Game Tests (26.x)\n\nUse Fabric Loom's dedicated Game Test source set. Configure it in the existing\n`fabricApi` block and keep its metadata and code under `src\u002Fgametest`, separate\nfrom ordinary unit tests.\n\n```groovy\nfabricApi {\n    configureTests {\n        createSourceSet = true\n        modId = \"example-mod-test-${project.name}\"\n        enableGameTests = true\n        enableClientGameTests = true\n        eula = true\n    }\n}\n```\n\nPlace `fabric.mod.json` in `src\u002Fgametest\u002Fresources\u002F` and register server tests\nunder `fabric-gametest`; use `fabric-client-gametest` for client tests. Implement\nserver methods with Fabric's `net.fabricmc.fabric.api.gametest.v1.GameTest` and,\nwhen setup is needed before a method runs, `CustomTestMethodInvoker`.\n\n```java\npackage com.example.mymod;\n\nimport java.lang.reflect.Method;\nimport net.fabricmc.fabric.api.gametest.v1.CustomTestMethodInvoker;\nimport net.fabricmc.fabric.api.gametest.v1.GameTest;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\n\npublic final class ExampleGameTest implements CustomTestMethodInvoker {\n    @GameTest\n    public void testBlock(GameTestHelper context) {\n        context.assertBlockPresent(Blocks.AIR, 0, 0, 0);\n        context.succeed();\n    }\n\n    @Override\n    public void invokeTestMethod(GameTestHelper context, Method method)\n            throws ReflectiveOperationException {\n        method.invoke(this, context);\n    }\n}\n```\n\n### `src\u002Fgametest\u002Fresources\u002Ffabric.mod.json`\n```json\n{\n  \"entrypoints\": {\n    \"fabric-gametest\": [\n      \"com.example.mymod.ExampleGameTest\"\n    ]\n  }\n}\n```\n\nKeep the `fabric-gametest` entrypoint in sync with the concrete Game Test class.\nFabric's server Game Tests run with `build`; use `runClientGameTest` for client\ntests. Follow the current Fabric documentation for project-specific Loom options\nand headless client CI.\n\n---\n\n## Legacy NeoForge Game Tests (1.21.3 only)\n\nKeep annotation-based tests isolated to Minecraft 1.21.3. The class can\nbe registered by either `@GameTestHolder(MOD_ID)` or a\n`RegisterGameTestsEvent` listener. Do not register a `@GameTestHolder` class\nagain with `modEventBus.register(MyGameTests.class)`.\n\n```java\nimport net.minecraft.gametest.framework.GameTest;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.neoforged.neoforge.gametest.GameTestHolder;\nimport net.neoforged.neoforge.gametest.PrefixGameTestTemplate;\n\n@GameTestHolder(\"examplemod\")\n@PrefixGameTestTemplate(false)\npublic final class ExampleGameTests {\n    @GameTest(template = \"example_structure\")\n    public static void smoke(GameTestHelper helper) {\n        helper.succeed();\n    }\n}\n```\n\nFor `RegisterGameTestsEvent`, register the class on the mod event bus and set\n`templateNamespace = MOD_ID` on each `@GameTest`. Legacy templates are `.nbt`\nfiles under `data\u002F\u003Cnamespace>\u002Fstructure\u002F`; `@PrefixGameTestTemplate(false)`\ncontrols whether the class name is added to the template path. When `template`\nis omitted, the path uses the lowercase method name and, unless that prefix is\ndisabled, the lowercase simple class name followed by a dot. `template` is the\npath name only; configure its namespace through `templateNamespace` or\n`@GameTestHolder`.\n\n---\n\n## CI\n\nKeep fast unit\u002Fmock tests separate from a loader's Game Test task, and select\nthe Java version for each Minecraft version: 25 for 26.x, 21 for 1.21.x, and 17 for Forge 1.20.1.\nUpload test reports when a runtime-facing job fails. Do not assume a task name\nfrom another loader: Fabric server Game Tests run with `build`, while NeoForge\nuses `runGameTestServer`. MockBukkit does not prove Folia thread safety or real\nserver bootstrap.\n\n---\n\n## References\n\n- MockBukkit GitHub: https:\u002F\u002Fgithub.com\u002FMockBukkit\u002FMockBukkit\n- MockBukkit docs: https:\u002F\u002Fdocs.mockbukkit.org\u002F\n- Fabric automated testing: https:\u002F\u002Fdocs.fabricmc.net\u002Fdevelop\u002Fautomatic-testing\n- NeoForge 26.x Game Tests: https:\u002F\u002Fdocs.neoforged.net\u002Fdocs\u002Fmisc\u002Fgametest\u002F\n- NeoForge 1.21.3 Game Tests: https:\u002F\u002Fdocs.neoforged.net\u002Fdocs\u002F1.21.3\u002Fmisc\u002Fgametest\u002F\n- JUnit Jupiter user guide: https:\u002F\u002Fjunit.org\u002Fjunit5\u002Fdocs\u002Fcurrent\u002Fuser-guide\u002F\n","# Minecraft Testing Skill\n\n## Testing Strategies Overview\n\n| Approach | Best For | Requires Game? |\n|----------|---------|----------------|\n| **JUnit Jupiter** (pure unit tests) | Logic, data structures, NBT serialization | No |\n| **MockBukkit** | Bukkit\u002FPaper plugin events, commands, inventory | No (mocked server) |\n| **NeoForge GameTests** | In-game block\u002Fentity\u002Fworld interaction | Yes (test environment) |\n| **Fabric GameTests** | In-game block\u002Fentity\u002Fworld interaction | Yes (test environment) |\n| **Integration server** | Full plugin\u002Fmod lifecycle | Yes (dedicated test server) |\n\nUse Java 25 for current 26.x projects. Keep legacy 1.21.x examples on Java 21\nand Forge 1.20.1 on Java 17. Do not combine source layouts or APIs across versions.\n\n### Routing Boundaries\n- `Use when`: the task is designing or implementing automated tests (unit, mock, gametest, CI test jobs) for Minecraft projects.\n- `Do not use when`: the task is implementing gameplay features rather than testing them (`minecraft-modding`, `minecraft-plugin-dev`, `minecraft-datapack`).\n- `Do not use when`: the task is release automation or publishing pipelines (`minecraft-ci-release`).\n\n## Bundled References And Helpers\n\n- Layout guide: `references\u002Ftest-layouts.md`\n- Fixture\u002Flayout validator: `.\u002Fscripts\u002Fvalidate-test-layout.sh --root \u003Cproject>`\n\nUse the validator before copying a test layout into a real project. It checks\nvisible static dependencies, metadata, and literal structure references. It does\nnot compile the project or prove a Game Test can run.\n\n---\n\n## Unit Testing (JUnit Jupiter — No Minecraft)\n\n### JUnit Platform task\n```kotlin\ntasks.test {\n    useJUnitPlatform()\n    testLogging {\n        events(\"passed\", \"skipped\", \"failed\")\n    }\n}\n```\n\nFor Fabric code that needs loader setup, use Fabric Loader JUnit rather than\nassuming ordinary JUnit initialized Minecraft:\n\n```groovy\ndependencies {\n    testImplementation \"net.fabricmc:fabric-loader-junit:${project.loader_version}\"\n}\n\ntest {\n    useJUnitPlatform()\n}\n```\n\nWhen a unit test reaches registry-dependent Minecraft classes, initialize only\nthe required bootstrap in test setup. The current Fabric guide uses\n`SharedConstants.tryDetectVersion()` and `Bootstrap.bootStrap()` for that case.\n\n### Example pure unit test\n```java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CooldownManagerTest {\n\n    @Test\n    void cooldown_tracksPlayersIndependently() {\n        var manager = new CooldownManager(500L); \u002F\u002F 500ms cooldown\n        manager.startCooldown(\"steve\");\n        assertTrue(manager.isOnCooldown(\"steve\"));\n        assertFalse(manager.isOnCooldown(\"notExisting\"));\n    }\n\n    @Test\n    void cooldown_throwsIllegalArgument_onNegativeDuration() {\n        assertThrows(IllegalArgumentException.class,\n            () -> new CooldownManager(-1L));\n    }\n}\n```\n\n---\n\n## MockBukkit (Paper\u002FBukkit Plugin Tests)\n\nThe paired dependencies below are a retained 26.2 example. For 26.3, verify\na published MockBukkit artifact for that exact Paper line before changing either\ndependency; a 26.2 mock does not establish 26.3 server behavior.\n\n### `build.gradle.kts`\n```kotlin\nrepositories {\n    maven(\"https:\u002F\u002Frepo.papermc.io\u002Frepository\u002Fmaven-public\u002F\")\n    mavenCentral()\n}\n\ndependencies {\n    compileOnly(\"io.papermc.paper:paper-api:26.2.build.+\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter:6.1.3\")\n    testImplementation(\"org.mockbukkit.mockbukkit:mockbukkit-v26.2:4.116.1\")\n    testRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n}\n```\n\n### Setup \u002F teardown pattern\n```java\nimport org.mockbukkit.mockbukkit.MockBukkit;\nimport org.mockbukkit.mockbukkit.ServerMock;\nimport org.mockbukkit.mockbukkit.entity.PlayerMock;\nimport org.junit.jupiter.api.*;\n\nclass MyPluginTest {\n\n    private ServerMock server;\n    private MyPlugin plugin;\n\n    @BeforeEach\n    void setUp() {\n        \u002F\u002F Start mock Bukkit server and load your plugin\n        server = MockBukkit.mock();\n        plugin = MockBukkit.load(MyPlugin.class);\n    }\n\n    @AfterEach\n    void tearDown() {\n        MockBukkit.unmock();\n    }\n}\n```\n\n### Testing events\n```java\n@Test\nvoid playerJoin_getsWelcomeMessage() {\n    PlayerMock player = server.addPlayer(\"Steve\"); \u002F\u002F fires PlayerJoinEvent\n\n    \u002F\u002F Assert the message sent by the plugin's join listener.\n    player.assertSaid(\"Welcome, Steve!\");\n}\n\n@Test\nvoid onBlockBreak_cancelledForNonOp() {\n    PlayerMock player = server.addPlayer();\n    player.setOp(false);\n\n    Block block = player.getWorld().getBlockAt(0, 64, 0);\n    block.setType(Material.STONE);\n    BlockBreakEvent event = new BlockBreakEvent(block, player);\n    server.getPluginManager().callEvent(event);\n\n    assertTrue(event.isCancelled(), \"Non-op should not be able to break blocks\");\n}\n```\n\n### Testing commands\n```java\n@Test\nvoid mypluginInfo_returnsVersion() {\n    PlayerMock player = server.addPlayer(\"Admin\");\n    player.setOp(true);\n\n    boolean result = server.dispatchCommand(player, \"myplugin info\");\n\n    assertTrue(result);\n    player.assertSaid(\"Version: \" + plugin.getDescription().getVersion());\n}\n\n@Test\nvoid mypluginReload_requiresAdminPermission() {\n    PlayerMock player = server.addPlayer(\"NonOp\");\n    player.setOp(false);\n\n    server.dispatchCommand(player, \"myplugin reload\");\n\n    player.assertSaid(\"No permission.\");\n}\n```\n\n### Testing inventory \u002F items\n```java\n@Test\nvoid giveKitCommand_givesPlayerItems() {\n    PlayerMock player = server.addPlayer();\n    \n    server.dispatchCommand(player, \"kit starter\");\n    \n    \u002F\u002F Check inventory\n    assertTrue(player.getInventory().contains(Material.STONE_SWORD));\n    assertTrue(player.getInventory().contains(Material.BREAD, 16));\n}\n```\n\n### Testing scheduler tasks\n```java\n@Test\nvoid repeatingTask_firesAfterDelay() {\n    PlayerMock player = server.addPlayer();\n    \n    \u002F\u002F Execute 40 ticks worth of scheduled tasks\n    server.getScheduler().performTicks(40L);\n    \n    \u002F\u002F Assert expected side effect happened\n    assertEquals(2, plugin.getTaskCount());\n}\n```\n\n### Testing Folia-safe scheduler abstractions\n\nMockBukkit does not emulate Folia's region-threaded runtime. The safe pattern is to\nwrap scheduling behind your own interface and unit test the abstraction boundary.\n\n```java\ninterface SchedulerFacade {\n    void runPlayerTask(Player player, Runnable task);\n    void runAsync(Runnable task);\n}\n\n@Test\nvoid playerTask_delegatesThroughFacade() {\n    List\u003CString> calls = new ArrayList\u003C>();\n    SchedulerFacade facade = new SchedulerFacade() {\n        @Override\n        public void runPlayerTask(Player player, Runnable task) {\n            calls.add(\"player\");\n            task.run();\n        }\n\n        @Override\n        public void runAsync(Runnable task) {\n            calls.add(\"async\");\n            task.run();\n        }\n    };\n\n    facade.runPlayerTask(server.addPlayer(), () -> calls.add(\"ran\"));\n    assertEquals(List.of(\"player\", \"ran\"), calls);\n}\n```\n\n### Testing PDC\n```java\nimport java.util.ArrayList;\nimport org.bukkit.damage.DamageSource;\nimport org.bukkit.damage.DamageType;\nimport org.bukkit.entity.LivingEntity;\n\n@Test\nvoid pdcKillCount_incrementsOnKill() {\n    PlayerMock player = server.addPlayer();\n    NamespacedKey key = new NamespacedKey(plugin, \"kills\");\n\n    \u002F\u002F EntityDeathEvent requires a living victim and an explicit damage source.\n    LivingEntity victim = (LivingEntity) server.addMockEntity(EntityType.ZOMBIE);\n    DamageSource damageSource = DamageSource.builder(DamageType.GENERIC)\n        .withCausingEntity(player)\n        .withDirectEntity(player)\n        .build();\n    EntityDeathEvent deathEvent = new EntityDeathEvent(\n        victim, damageSource, new ArrayList\u003C>(), 0\n    );\n    server.getPluginManager().callEvent(deathEvent);\n\n    int kills = player.getPersistentDataContainer()\n        .getOrDefault(key, PersistentDataType.INTEGER, 0);\n    assertEquals(1, kills);\n}\n```\n\nThis dispatches a synthetic death event. For player attribution, the listener\nunder test should read `event.getDamageSource().getCausingEntity()`; test actual\ncombat attribution separately on a real server.\n\n### Testing item or chunk PDC writes\n```java\n@Test\nvoid itemPdc_roundTripsCustomId() {\n    NamespacedKey key = new NamespacedKey(plugin, \"custom_id\");\n    ItemStack item = new ItemStack(Material.STICK);\n\n    item.editMeta(meta -> meta.getPersistentDataContainer().set(\n        key, PersistentDataType.STRING, \"wand\"\n    ));\n\n    String value = item.getItemMeta().getPersistentDataContainer()\n        .get(key, PersistentDataType.STRING);\n    assertEquals(\"wand\", value);\n}\n```\n\n---\n\n## Current NeoForge Game Tests (26.x)\n\nNeoForge 1.21.5 and later uses data-driven test environments and test instances,\nnot the old `@GameTestHolder` method-registration API. Store resources under\n`data\u002F\u003Cnamespace>\u002Ftest_environment\u002F` and `data\u002F\u003Cnamespace>\u002Ftest_instance\u002F`.\nA `test_instance` selects its environment, structure, timing, and either a\nregistered function or a block-based test.\n\n```json\n{\n  \"environment\": \"minecraft:default\",\n  \"structure\": \"examplemod:example_structure\",\n  \"max_ticks\": 200,\n  \"setup_ticks\": 0,\n  \"required\": true,\n  \"type\": \"minecraft:function\",\n  \"function\": \"examplemod:example_function\"\n}\n```\n\nRegister the `Consumer\u003CGameTestHelper>` with a `DeferredRegister` for the\ncurrent `BuiltInRegistries.TEST_FUNCTION` registry, then attach that register to\nthe mod event bus. The function below makes the JSON reference above usable.\nUse `RegisterGameTestsEvent` only when registering environments and test\ninstances in code instead of data files. Keep the referenced structure in\n`data\u002F\u003Cnamespace>\u002Fstructure\u002F\u003Cpath>.nbt` and mark success explicitly.\n\n```java\nimport java.util.function.Consumer;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\nimport net.neoforged.bus.api.IEventBus;\nimport net.neoforged.fml.common.Mod;\nimport net.neoforged.neoforge.registries.DeferredHolder;\nimport net.neoforged.neoforge.registries.DeferredRegister;\n\n@Mod(ExampleGameTests.MOD_ID)\npublic final class ExampleGameTests {\n    public static final String MOD_ID = \"examplemod\";\n    private static final DeferredRegister\u003CConsumer\u003CGameTestHelper>> TEST_FUNCTIONS =\n        DeferredRegister.create(BuiltInRegistries.TEST_FUNCTION, MOD_ID);\n    public static final DeferredHolder\u003CConsumer\u003CGameTestHelper>, Consumer\u003CGameTestHelper>>\n        EXAMPLE_FUNCTION = TEST_FUNCTIONS.register(\n            \"example_function\", () -> ExampleGameTests::exampleTest\n        );\n\n    public ExampleGameTests(IEventBus modBus) {\n        TEST_FUNCTIONS.register(modBus);\n    }\n\n    public static void exampleTest(GameTestHelper helper) {\n        helper.assertBlockPresent(Blocks.AIR, 0, 0, 0);\n        helper.succeed();\n    }\n}\n```\n\nRun `.\u002Fgradlew runGameTestServer`; the server exits with the count of required\nfailed tests. This command is for a real project, not this skills repository.\n\n---\n\n## Current Fabric Game Tests (26.x)\n\nUse Fabric Loom's dedicated Game Test source set. Configure it in the existing\n`fabricApi` block and keep its metadata and code under `src\u002Fgametest`, separate\nfrom ordinary unit tests.\n\n```groovy\nfabricApi {\n    configureTests {\n        createSourceSet = true\n        modId = \"example-mod-test-${project.name}\"\n        enableGameTests = true\n        enableClientGameTests = true\n        eula = true\n    }\n}\n```\n\nPlace `fabric.mod.json` in `src\u002Fgametest\u002Fresources\u002F` and register server tests\nunder `fabric-gametest`; use `fabric-client-gametest` for client tests. Implement\nserver methods with Fabric's `net.fabricmc.fabric.api.gametest.v1.GameTest` and,\nwhen setup is needed before a method runs, `CustomTestMethodInvoker`.\n\n```java\npackage com.example.mymod;\n\nimport java.lang.reflect.Method;\nimport net.fabricmc.fabric.api.gametest.v1.CustomTestMethodInvoker;\nimport net.fabricmc.fabric.api.gametest.v1.GameTest;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.minecraft.world.level.block.Blocks;\n\npublic final class ExampleGameTest implements CustomTestMethodInvoker {\n    @GameTest\n    public void testBlock(GameTestHelper context) {\n        context.assertBlockPresent(Blocks.AIR, 0, 0, 0);\n        context.succeed();\n    }\n\n    @Override\n    public void invokeTestMethod(GameTestHelper context, Method method)\n            throws ReflectiveOperationException {\n        method.invoke(this, context);\n    }\n}\n```\n\n### `src\u002Fgametest\u002Fresources\u002Ffabric.mod.json`\n```json\n{\n  \"entrypoints\": {\n    \"fabric-gametest\": [\n      \"com.example.mymod.ExampleGameTest\"\n    ]\n  }\n}\n```\n\nKeep the `fabric-gametest` entrypoint in sync with the concrete Game Test class.\nFabric's server Game Tests run with `build`; use `runClientGameTest` for client\ntests. Follow the current Fabric documentation for project-specific Loom options\nand headless client CI.\n\n---\n\n## Legacy NeoForge Game Tests (1.21.3 only)\n\nKeep annotation-based tests isolated to Minecraft 1.21.3. The class can\nbe registered by either `@GameTestHolder(MOD_ID)` or a\n`RegisterGameTestsEvent` listener. Do not register a `@GameTestHolder` class\nagain with `modEventBus.register(MyGameTests.class)`.\n\n```java\nimport net.minecraft.gametest.framework.GameTest;\nimport net.minecraft.gametest.framework.GameTestHelper;\nimport net.neoforged.neoforge.gametest.GameTestHolder;\nimport net.neoforged.neoforge.gametest.PrefixGameTestTemplate;\n\n@GameTestHolder(\"examplemod\")\n@PrefixGameTestTemplate(false)\npublic final class ExampleGameTests {\n    @GameTest(template = \"example_structure\")\n    public static void smoke(GameTestHelper helper) {\n        helper.succeed();\n    }\n}\n```\n\nFor `RegisterGameTestsEvent`, register the class on the mod event bus and set\n`templateNamespace = MOD_ID` on each `@GameTest`. Legacy templates are `.nbt`\nfiles under `data\u002F\u003Cnamespace>\u002Fstructure\u002F`; `@PrefixGameTestTemplate(false)`\ncontrols whether the class name is added to the template path. When `template`\nis omitted, the path uses the lowercase method name and, unless that prefix is\ndisabled, the lowercase simple class name followed by a dot. `template` is the\npath name only; configure its namespace through `templateNamespace` or\n`@GameTestHolder`.\n\n---\n\n## CI\n\nKeep fast unit\u002Fmock tests separate from a loader's Game Test task, and select\nthe Java version for each Minecraft version: 25 for 26.x, 21 for 1.21.x, and 17 for Forge 1.20.1.\nUpload test reports when a runtime-facing job fails. Do not assume a task name\nfrom another loader: Fabric server Game Tests run with `build`, while NeoForge\nuses `runGameTestServer`. MockBukkit does not prove Folia thread safety or real\nserver bootstrap.\n\n---\n\n## References\n\n- MockBukkit GitHub: https:\u002F\u002Fgithub.com\u002FMockBukkit\u002FMockBukkit\n- MockBukkit docs: https:\u002F\u002Fdocs.mockbukkit.org\u002F\n- Fabric automated testing: https:\u002F\u002Fdocs.fabricmc.net\u002Fdevelop\u002Fautomatic-testing\n- NeoForge 26.x Game Tests: https:\u002F\u002Fdocs.neoforged.net\u002Fdocs\u002Fmisc\u002Fgametest\u002F\n- NeoForge 1.21.3 Game Tests: https:\u002F\u002Fdocs.neoforged.net\u002Fdocs\u002F1.21.3\u002Fmisc\u002Fgametest\u002F\n- JUnit Jupiter user guide: https:\u002F\u002Fjunit.org\u002Fjunit5\u002Fdocs\u002Fcurrent\u002Fuser-guide\u002F\n",{"name":5,"description":7},"main","22d61ce6b7a0c5f215008824aa6b94ecc73f2a43","plugins\u002Fminecraft-codex-skills\u002Fskills\u002Fminecraft-testing",11,"https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills","https:\u002F\u002Fgithub.com\u002FJahrome907","joe-o.com","Product engineer building field tools, workflow automation, and polished web applications.","2026-10-30 08:19:45",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add Jahrome907\u002Fminecraft-agent-skills --skill minecraft-testing",{"tool":63,"command":64},"bunx","bunx skills add Jahrome907\u002Fminecraft-agent-skills --skill minecraft-testing",{"tool":66,"command":67},"pnpm","pnpm dlx skills add Jahrome907\u002Fminecraft-agent-skills --skill minecraft-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fminecraft-codex-skills\u002Fskills\u002Fminecraft-testing\u002FSKILL.md",true,[71,77],{"path":72,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":75,"sourceHtmlUrl":76},"references\u002Ftest-layouts.md","REFERENCE","参考文档",3068,"https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fminecraft-codex-skills\u002Fskills\u002Fminecraft-testing\u002Freferences\u002Ftest-layouts.md",{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"scripts\u002Fvalidate-test-layout.sh","SCRIPT","脚本",15285,"https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fminecraft-codex-skills\u002Fskills\u002Fminecraft-testing\u002Fscripts\u002Fvalidate-test-layout.sh",[84,99,104,114,122,129],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":90,"publishedAt":43},"01M6D2TV8CBAMB70CEC19VMVX5","minecraft-ci-release","Fixture with valid workflow YAML and a warning-only glob pattern.","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fwarn-only\u002FSKILL.md",[],[91,25,92,37,93,94,95,96,97,98],"minecraft ci release","ci","fixture","pattern","only","yaml","workflow","valid",{"skillNo":100,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":43},"01M6D2TR1N9TNJPTRB66WDCY8X","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fvalid\u002FSKILL.md",[],[98],{"skillNo":105,"name":86,"nameZh":6,"description":106,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":112,"publishedAt":43},"01M6D2TNBM97CCTYFCDKCFQ4AR","Fixture for a secretless workflow that passes strict validation.","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fsecretless-strict\u002FSKILL.md",[109],{"categoryNo":110,"categoryName":111,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[91,25,92,37,93,113,97],"validation",{"skillNo":115,"name":116,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":120,"publishedAt":43},"01M6D2TJ9FX4FH1TMGKQPYNZQX","non-workflow-yaml","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fnon-workflow-yaml\u002FSKILL.md",[119],{"categoryNo":110,"categoryName":111,"taxonomyType":21,"taxonomyTypeDesc":22},[121,96,97],"non workflow yaml",{"skillNo":123,"name":86,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":43},"01M6D2TFE807BSXTEREYPWFY3G","Fixture with a mutable remote action reference.","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fmutable-action\u002FSKILL.md",[],[91,25,92,37,128,93],"reference",{"skillNo":130,"name":86,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":134,"publishedAt":43},"01M6D2TBYJ0SM3B79908DH9ECE","Fixture with valid multiline flow collections in workflow YAML.","https:\u002F\u002Fgithub.com\u002FJahrome907\u002Fminecraft-agent-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fvalidators\u002Fci-release\u002Fmultiline-flow\u002FSKILL.md",[],[91,37,25,92,97,96,135,93,136,98],"flow","collections"]