🇷🇺 Русский (Russian)
🇷🇺 Русский (Russian)
Внешний вид
🇷🇺 Русский (Russian)
🇷🇺 Русский (Russian)
Внешний вид
This page is written for version:
1.21
This page is written for version:
1.21
На этой странице объясняется, как написать код для автоматического тестирования частей вашего мода. Существует два способа автоматического тестирования вашего мода: модульные тесты с помощью Fabric Loader JUnit или игровые тесты с помощью фреймворка Gametest из Minecraft.
Модульные тесты следует использовать для тестирования компонентов вашего кода, таких как методы и вспомогательные классы, в то время как игровые тесты запускают реальный клиент и сервер Minecraft для запуска ваших тестов, что делает их подходящими для тестирования функций и игрового процесса.
WARNING
В настоящее время данное руководство охватывает только модульное тестирование.
Поскольку моддинг Minecraft основан на инструментах модификации байт-кода во время выполнения, таких как Mixin, простое добавление и использование JUnit обычно не работает. Вот почему Fabric предоставляет Fabric Loader JUnit — плагин JUnit, который позволяет проводить модульное тестирование в Minecraft.
Сначала нам необходимо добавить Fabric Loader JUnit в среду разработки. Добавьте следующее в блок зависимостей в build.gradle
:
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
Затем нам нужно указать Gradle использовать Fabric Loader JUnit для тестирования. Это можно сделать, добавив следующий код в build.gradle
:
test {
useJUnitPlatform()
}
После перезагрузки Gradle вы будете готовы писать тесты.
Эти тесты пишутся так же, как и обычные тесты JUnit, с небольшой дополнительной настройкой, если вам нужно получить доступ к любому классу, зависящему от реестра, например ItemStack
. Если вы знакомы с JUnit, можете перейти к разделу Настройка реестров.
Тесты пишутся в каталоге src/test/java
.
Одним из правил именования является отражение структуры пакета тестируемого класса. Например, чтобы протестировать src/main/java/com/example/docs/codec/BeanType.java
, вам нужно создать класс в src/test/java/com/example/docs/codec/BeanTypeTest.java
. Обратите внимание, как мы добавили Test
в конец имени класса. Это также позволяет легко получить доступ к методам и полям, закрытым для пакета.
Другое соглашение об именовании — наличие пакета test
, например src/test/java/com/example/docs/test/codec/BeanTypeTest.java
. Это предотвращает некоторые проблемы, которые могут возникнуть при использовании того же пакета, если вы используете модули Java.
После создания тестового класса используйте ⌘/CTRLN, чтобы вызвать меню «Создать». Выберите Test и начните вводить имя метода, обычно начинающееся с test
. Выберите Test и начните вводить имя метода, обычно начинающееся с test
. Нажмите ENTER, когда закончите. Дополнительные советы и рекомендации по использованию IDE см. в разделе Советы и рекомендации по IDE.
Конечно, вы можете написать сигнатуру метода вручную, и любой метод экземпляра без параметров и с типом возвращаемого значения void будет идентифицирован как тестовый метод. В итоге у вас должно получиться следующее:
Обратите внимание на зеленые стрелки индикаторы в желобе: вы можете легко запустить тест, щелкнув по ним. В качестве альтернативы ваши тесты будут запускаться автоматически для каждой сборки, включая сборки CI такие, как GitHub Actions. Если вы используете GitHub Actions, не забудьте прочитать Настройка GitHub Actions.
Теперь пришло время написать настоящий тестовый код. Вы можете утверждать условия, используя org.junit.jupiter.api.Assertions
. Проверьте следующий тест:
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());
}
}
Объяснение того, что на самом деле делает этот код, см. в Кодеки.
Отлично, первый тест сработал! Но подождите, второй тест не прошёл? В журналах мы получаем одну из следующих ошибок.
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)
Это происходит потому, что мы пытаемся получить доступ к реестру или классу, который зависит от реестра (или, в редких случаях, зависит от других классов Minecraft таких, как SharedConstants
), но Minecraft не инициализирован. Нам нужно просто немного инициализировать его, чтобы реестры заработали. Просто добавьте следующий код в начало метода beforeAll
.
SharedConstants.createGameVersion();
Bootstrap.initialize();
INFO
В этом разделе предполагается, что вы используете стандартный рабочий процесс GitHub Action, включенный в пример мода и в шаблон мода.
Теперь ваши тесты будут запускаться для каждой сборки, включая сборки поставщиков непрерывной интеграции, таких как GitHub Actions. Но что делать, если сборка не удалась? Нам необходимо загрузить журналы как артефакт, чтобы мы могли просматривать отчеты об испытаниях.
Добавьте это в файл .github/workflows/build.yml
, ниже шага ./gradlew build
.
- name: Store reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: reports
path: |
**/build/reports/
**/build/test-results/