agentsclimarketplace

Generate test suite

Skill MrPippi/MJP-Claude-Skills/.cursor/skills/generate-test-suite

依插件主類與功能描述,產生 JUnit 5 + MockBukkit 單元測試套件骨架,含 pom.xml 依賴設定、MockBukkit 伺服器初始化樣板、常見的玩家/事件/指令測試範例。當使用者說「幫我寫測試」、「MockBukkit 怎麼用」、「JUnit 5 插件測試」、「單元測試骨架」時自動應用。From its SKILL.md

Install
npx -y skills add MrPippi/MJP-Claude-Skills --skill generate-test-suite

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 3 commands, including `mvn test` and 2 more.

SKILL.md

8.7 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it

Generate Test Suite Skill

目標

為 Bukkit/Paper 插件產生可直接執行的 JUnit 5 + MockBukkit 測試套件,涵蓋插件啟用/停用、事件觸發、指令執行、設定讀取等常見測試情境。


使用流程

  1. 確認 MockBukkit 版本:依 MC 版本選擇對應的 MockBukkit
  2. 更新 pom.xml:加入測試依賴
  3. 產生測試類別:依功能需求產生對應的測試骨架
  4. 說明執行方式:mvn test

MockBukkit 版本對照

Minecraft 版本MockBukkit 版本
1.21.xMockBukkit:v1.21:3.x.x
1.20.xMockBukkit:v1.20:3.x.x
1.19.xMockBukkit:v1.19:3.x.x

pom.xml 測試依賴

在 pom.xml 的 <dependencies> 區段加入:

<!-- JUnit 5 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.11.0</version>
    <scope>test</scope>
</dependency>

<!-- MockBukkit(依 MC 版本調整) -->
<dependency>
    <groupId>com.github.seeseemelk</groupId>
    <artifactId>MockBukkit-v1.21</artifactId>
    <version>3.131.0</version>
    <scope>test</scope>
</dependency>

在 <build><plugins> 區段加入 Surefire 插件以執行 JUnit 5:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.5.0</version>
</plugin>

MockBukkit 需要 JitPack repository:

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

代碼範本

基礎插件生命週期測試

package com.example.myplugin;

import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MyPluginTest {

    private ServerMock server;
    private MyPlugin plugin;

    @BeforeEach
    void setUp() {
        server = MockBukkit.mock();
        plugin = MockBukkit.load(MyPlugin.class);
    }

    @AfterEach
    void tearDown() {
        MockBukkit.unmock();
    }

    @Test
    void testPluginEnables() {
        assertTrue(plugin.isEnabled(), "插件應成功啟用");
    }

    @Test
    void testPluginHasCorrectName() {
        assertEquals("MyPlugin", plugin.getName());
    }

    @Test
    void testDefaultConfigLoaded() {
        assertNotNull(plugin.getConfig());
        assertFalse(plugin.getConfig().getBoolean("general.debug"));
    }
}

事件觸發測試

package com.example.myplugin.listeners;

import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.PlayerMock;
import com.example.myplugin.MyPlugin;
import org.bukkit.event.player.PlayerJoinEvent;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class PlayerListenerTest {

    private ServerMock server;
    private MyPlugin plugin;

    @BeforeEach
    void setUp() {
        server = MockBukkit.mock();
        plugin = MockBukkit.load(MyPlugin.class);
    }

    @AfterEach
    void tearDown() {
        MockBukkit.unmock();
    }

    @Test
    void testPlayerJoinSendsWelcomeMessage() {
        PlayerMock player = server.addPlayer("TestPlayer");

        // 觸發加入事件
        PlayerJoinEvent event = new PlayerJoinEvent(player, null);
        server.getPluginManager().callEvent(event);

        // 驗證玩家收到訊息(如有)
        // player.assertSaid("歡迎加入!");
        assertTrue(player.isOnline());
    }

    @Test
    void testNewPlayerReceivesWelcome() {
        PlayerMock player = server.addPlayer();

        // 模擬首次登入(MockBukkit 預設 hasPlayedBefore = false)
        assertFalse(player.hasPlayedBefore());

        PlayerJoinEvent event = new PlayerJoinEvent(player, null);
        server.getPluginManager().callEvent(event);

        player.assertSaid("歡迎首次加入!");
    }
}

指令執行測試

package com.example.myplugin.commands;

import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.PlayerMock;
import com.example.myplugin.MyPlugin;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MyPluginCommandTest {

    private ServerMock server;
    private MyPlugin plugin;

    @BeforeEach
    void setUp() {
        server = MockBukkit.mock();
        plugin = MockBukkit.load(MyPlugin.class);
    }

    @AfterEach
    void tearDown() {
        MockBukkit.unmock();
    }

    @Test
    void testCommandNoPermission() {
        PlayerMock player = server.addPlayer();
        // 確保沒有權限
        player.addAttachment(plugin, "myplugin.use", false);

        boolean result = server.dispatchCommand(player, "myplugin");

        player.assertSaid("你沒有權限執行此操作。");
    }

    @Test
    void testCommandWithPermission() {
        PlayerMock player = server.addPlayer();
        player.setOp(true);

        boolean result = server.dispatchCommand(player, "myplugin");
        assertTrue(result);
    }

    @Test
    void testReloadCommand() {
        PlayerMock player = server.addPlayer();
        player.setOp(true);

        server.dispatchCommand(player, "myplugin reload");
        player.assertSaid("配置已重新載入。");
    }

    @Test
    void testConsoleCannotUsePlayerOnlyCommand() {
        // 測試控制台執行限玩家指令
        var console = server.getConsoleSender();
        server.dispatchCommand(console, "myplugin");
        // 控制台應收到提示
        // console.assertSaid("此指令僅限玩家使用。");
    }
}

ConfigManager 測試

package com.example.myplugin.managers;

import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import com.example.myplugin.MyPlugin;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class ConfigManagerTest {

    private ServerMock server;
    private MyPlugin plugin;
    private ConfigManager configManager;

    @BeforeEach
    void setUp() {
        server = MockBukkit.mock();
        plugin = MockBukkit.load(MyPlugin.class);
        configManager = plugin.getConfigManager();
    }

    @AfterEach
    void tearDown() {
        MockBukkit.unmock();
    }

    @Test
    void testDefaultDebugIsFalse() {
        assertFalse(configManager.isDebug());
    }

    @Test
    void testDefaultStartingBalance() {
        assertEquals(1000.0, configManager.getStartingBalance(), 0.01);
    }

    @Test
    void testDefaultLanguage() {
        assertEquals("zh-tw", configManager.getLanguage());
    }
}

測試目錄結構

src/
└── test/
    └── java/
        └── com/example/myplugin/
            ├── MyPluginTest.java           ← 主類生命週期
            ├── commands/
            │   └── MyPluginCommandTest.java
            ├── listeners/
            │   └── PlayerListenerTest.java
            └── managers/
                └── ConfigManagerTest.java

執行測試

# 執行所有測試
mvn test

# 執行特定測試類
mvn test -Dtest=MyPluginTest

# 執行含特定關鍵字的測試
mvn test -Dtest="*Command*"

常見錯誤與修正

錯誤原因修正
IllegalStateException: MockBukkit is not running未呼叫 MockBukkit.mock()確認 @BeforeEach 有 MockBukkit.mock()
@AfterEach 未呼叫 unmock() 導致下個測試失敗伺服器狀態殘留每個測試類的 @AfterEach 必須呼叫 MockBukkit.unmock()
NoSuchCommandException指令未在 plugin.yml 宣告確認 plugin.yml 有對應的 commands: 節點
player.assertSaid() 失敗訊息格式不符(MiniMessage 標籤未渲染)比對原始 MiniMessage 字串或使用 assertSaidRaw()
Maven 找不到 MockBukkit未加入 JitPack repository在 pom.xml 加入 https://jitpack.io

What ships with it: 1 file

3.1 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.