🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
Diese Seite ist für folgende Version geschrieben:
1.21
Diese Seite ist für folgende Version geschrieben:
1.21
Diese Seite erklärt, wie man Code schreibt, um Teile des Mods automatisch zu testen. Es gibt zwei Möglichkeiten, deinen Mod automatisch zu testen: Unit Tests mit Fabric Loader JUnit oder Spieltests mit dem Gametest-Framework von Minecraft.
Unit-Tests sollten verwendet werden, um Komponenten deines Codes zu testen, wie z. B. Methoden und Hilfsklassen, während Spieltests einen tatsächlichen Minecraft-Client und -Server starten, um deine Tests auszuführen, was sie für das Testen von Funktionen und Gameplay geeignet macht.
WARNING
Aktuell deckt dieser Leitfaden nur Unit Tests ab.
Da Minecraft Modding auf Laufzeit-Bytecode-Modifikationswerkzeuge wie Mixin angewiesen ist, würde das einfache Hinzufügen und Verwenden von JUnit normalerweise nicht funktionieren. Aus diesem Grund bietet Fabric, Fabric Loader JUnit, ein JUnit-Plugin, das Unit-Tests in Minecraft ermöglicht.
Zuerst müssen wir Fabric Loader JUnit zu der Entwicklungsumgebung hinzufügen. Füge folgendes zu deinem Dependencies-Block in deiner build.gradle
-Datei hinzu:
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
Dann müssen wir Gradle anweisen, Fabric Loader JUnit zum Testen zu verwenden. Du kannst dies tun, indem du den folgenden Code zu deiner build.gradle
-Datei hinzufügst:
test {
useJUnitPlatform()
}
Nachdem du Gradle neu geladen hast, bist du bereit, Tests zu schreiben.
Diese Tests werden genau wie normale JUnit-Tests geschrieben, mit ein paar zusätzlichen Einstellungen, wenn du auf eine von einer Registry abhängigen Klasse wie ItemStack
zugreifen willst. Wenn du mit JUnit vertraut bist, kannst du zu Registries einrichten übergehen.
Tests werden im Verzeichnis src/test/java
geschrieben.
Eine Namenskonvention besteht darin, die Paketstruktur der Klasse, die du testest, widerzuspiegeln. Zum Beispiel, um src/main/java/com/example/docs/codec/BeanType.java
zu testen, würdest du eine Klasse src/test/java/com/example/docs/codec/BeanTypeTest.java
erstellen. Beachte, dass wir Test
an das Ende des Klassennamens angefügt haben. Dies ermöglicht dir den einfachen Zugang zu Paket-Privaten Methoden und Feldern.
Eine andere Namenskonvention ist ein test
-Paket, wie src/test/java/com/example/docs/test/codec/BeanTypeTest.java
. Dies verhindert einige Probleme, die bei der Verwendung desselben Pakets auftreten können, wenn du Java-Module verwendest.
Nachdem du die Testklasse erstellt hast, verwende ⌘/CTRLN, um das Generate-Menü aufzurufen. Wähle Test und beginne mit der Eingabe deines Methodennamens, der normalerweise mit test
beginnt. Drücke ENTER, wenn du fertig bist. Weitere Tipps und Tricks zur Verwendung der IDE findest du unter IDE Tipps und Tricks.
Du kannst die Methodensignatur natürlich auch von Hand schreiben, und jede Instanzmethode ohne Parameter und mit einem ungültigen Rückgabetyp wird als Testmethode identifiziert. Das Ergebnis sollte wie folgt aussehen:
Beachte die grünen Pfeilindikatoren in dem Zwischenraum: Du kannst ganz einfach einen Test ausführen, indem du sie anklickst. Alternativ dazu werden deine Tests automatisch bei jedem Build ausgeführt, einschließlich CI-Builds wie GitHub Actions. Wenn du GitHub Actions verwendest, solltest du unbedingt GitHub Actions einrichten lesen.
Nun ist es an der Zeit, den eigentlichen Testcode zu schreiben. Du kannst Bedingungen mit Hilfe von org.junit.jupiter.api.Assertions
prüfen. Siehe dazu den folgenden Test an:
public class BeanTypeTest {
private static final Gson GSON = new GsonBuilder().create();
@BeforeAll
static void beforeAll() {
BeanTypes.register();
}
@Test
void testBeanCodec() {
StringyBean expectedBean = new StringyBean("This bean is stringy!");
Bean actualBean = Bean.BEAN_CODEC.parse(JsonOps.INSTANCE, GSON.fromJson("{\"type\":\"example:stringy_bean\",\"stringy_string\":\"This bean is stringy!\"}", JsonObject.class)).getOrThrow();
Assertions.assertInstanceOf(StringyBean.class, actualBean);
Assertions.assertEquals(expectedBean.getType(), actualBean.getType());
Assertions.assertEquals(expectedBean.getStringyString(), ((StringyBean) actualBean).getStringyString());
}
@Test
void testDiamondItemStack() {
// I know this isn't related to beans, but I need an example :)
ItemStack diamondStack = new ItemStack(Items.DIAMOND, 65);
Assertions.assertTrue(diamondStack.isOf(Items.DIAMOND));
Assertions.assertEquals(65, diamondStack.getCount());
}
}
Für eine Erklärung, was dieser Code tatsächlich tut, siehe Codecs.
Großartig, der erste Test war erfolgreich! Aber Moment, der zweite Test ist fehlgeschlagen? In den Logs wird einer der folgenden Fehler angezeigt.
java.lang.ExceptionInInitializerError
at net.minecraft.item.ItemStack.<clinit>(ItemStack.java:94)
at com.example.docs.codec.BeanTypeTest.testBeanCodec(BeanTypeTest.java:20)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.IllegalArgumentException: Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event])
at net.minecraft.Bootstrap.createNotBootstrappedException(Bootstrap.java:118)
at net.minecraft.Bootstrap.ensureBootstrapped(Bootstrap.java:111)
at net.minecraft.registry.Registries.create(Registries.java:238)
at net.minecraft.registry.Registries.create(Registries.java:229)
at net.minecraft.registry.Registries.<clinit>(Registries.java:139)
... 5 more
Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event])
java.lang.IllegalArgumentException: Not bootstrapped (called from registry ResourceKey[minecraft:root / minecraft:game_event])
at net.minecraft.Bootstrap.createNotBootstrappedException(Bootstrap.java:118)
at net.minecraft.Bootstrap.ensureBootstrapped(Bootstrap.java:111)
at net.minecraft.registry.Registries.create(Registries.java:238)
at net.minecraft.registry.Registries.create(Registries.java:229)
at net.minecraft.registry.Registries.<clinit>(Registries.java:139)
at net.minecraft.item.ItemStack.<clinit>(ItemStack.java:94)
at com.example.docs.codec.BeanTypeTest.testBeanCodec(BeanTypeTest.java:20)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Dies liegt daran, dass wir versuchen, auf die Registry oder eine Klasse zuzugreifen, die von der Registry abhängt (oder, in seltenen Fällen, von anderen Minecraft-Klassen wie SharedConstants
), aber Minecraft wurde noch nicht initialisiert. Wir müssen es nur ein wenig initialisieren, damit die Registries funktionieren. Fügen einfach den folgenden Code an den Anfang deiner Methode beforeAll
.
SharedConstants.createGameVersion();
Bootstrap.initialize();
INFO
In diesem Abschnitt wird davon ausgegangen, dass du den standardmäßigen GitHub Action-Workflow verwendest, der in dem Beispielmod und in der Mod-Vorlage enthalten ist.
Deine Tests werden nun bei jedem Build ausgeführt, auch bei denen von CI-Anbietern wie GitHub Actions. Was aber, wenn ein Build fehlschlägt? Wir müssen die Logs als Artefakt hochladen, damit wir die Testberichte ansehen können.
Füge dies zu deiner .github/workflows/build.yml
-Datei hinzu, unterhalb des ./gradlew build
Schrittes.
- name: Store reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: reports
path: |
**/build/reports/
**/build/test-results/