Generate test suite
Skill MrPippi/MJP-Claude-Skills/.cursor/skills/generate-test-suite
Minecraft Java Plugin Claude Skills
npx -y skills add MrPippi/MJP-Claude-Skills --skill generate-test-suiteAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing 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.
What its author says it does
Copied from the file, not written here
依插件主類與功能描述,產生 JUnit 5 + MockBukkit 單元測試套件骨架,含 pom.xml 依賴設定、MockBukkit 伺服器初始化樣板、常見的玩家/事件/指令測試範例。當使用者說「幫我寫測試」、「MockBukkit 怎麼用」、「JUnit 5 插件測試」、「單元測試骨架」時自動應用。
SKILL.md
8.7 KB, as published. Nobody here has run it
Generate Test Suite Skill
目標
為 Bukkit/Paper 插件產生可直接執行的 JUnit 5 + MockBukkit 測試套件,涵蓋插件啟用/停用、事件觸發、指令執行、設定讀取等常見測試情境。
使用流程
- 確認 MockBukkit 版本:依 MC 版本選擇對應的 MockBukkit
- 更新 pom.xml:加入測試依賴
- 產生測試類別:依功能需求產生對應的測試骨架
- 說明執行方式:
mvn test
MockBukkit 版本對照
| Minecraft 版本 | MockBukkit 版本 |
|---|---|
| 1.21.x | MockBukkit:v1.21:3.x.x |
| 1.20.x | MockBukkit:v1.20:3.x.x |
| 1.19.x | MockBukkit: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 |