- Версия(и) Minecraft
- 1.7.10
MCInGameTester
Движок для тестов, которые требуют загруженный Minecraft.
Использование
Добавление в gradle
Просто добавьте следующую строку после
apply plugin: 'forge'
:
Gradle (Groovy):
apply from: 'https://raw.githubusercontent.com/MJaroslav/MCInGameTester/master/gradle/configurations/v1.gradle'
Обратите внимание: Это расширение gradle скрипта просто добавляет пару задач и последнюю версию движка в зависимости. Если вы хотите использовать определенную версию движка, добавьте следующие строки в gradle скрипт:
Gradle (Groovy):
configurations.all {
resolutionStrategy {
force 'com.github.MJaroslav:MCInGameTester:VERSION:dev'
}
}
Где
VERSION
выбранная вами версия.Задачи
testClient
- запускает клиент с движком и тестовым classpath'ом.testServer
- запускает сервер с движком и тестовым classpath'ом.testJar
- собирает временный jar архив из тестовых исходников. Является вспомогательной задачей и прямой вашей нужды в ней нет.
Объявление тестов
Все, что вам нужно для создания тестов, содержится в
com.github.mjaroslav.mcingametester.api
(API) пакете. Если вы хотите вывести что-либо в консоль, используйте com.github.mjaroslav.mcingametester.lib.ModInfo#LOG
.Тестоподобный метод - это нестатичный void метод без параметров.
Контейнер тестов - это простой класс с тестоподобными методами.
Обратите внимание: Все помеченные API аннотациями вещи могут иметь любой модификатор доступа.
Для начала, просто создайте контейнеры тестов в ваших тестовых исходниках и зачем пометьте их аннотацией
@Client
(только для стороны клиента), @Server
(только для стороны сервера) или @Common
(для обоих сторон).Обратите внимание: Для этих аннотаций вы можете использовать параметр
when
с типом LoadState
для установки стадии запуска игры во во время которой будут запущены тесты данного контейнера тестов.Теперь вы можете писать тесты в этих классах. Просто пишите тестоподобные методы и помечайте их с помощью
@Test
.Если вы хотите что-либо выполнить до или после каждого теста, вы можете пометить тестоподобный метод с помощью
@BeforeEach
или @AfterEach
вместо @Test
.Если вы хотите что-либо выполнить до или после всех тестов в классе, вы можете пометить статичный тестоподобный метод с помощью
@BeforeClass
или @AfterClass
вместо @Test
.В дополнение, в серверных тестах вы можете сделать нестатичное поле типа World и поменить его аннотацией
@WorldShadow
. Это установит в его значение серверный объект обычного мира (с id 0).Обратите внимание: По умолчанию в gradle заданиях тестов игры отображается только логгер из
ModInfo
.Примеры
Java:
// Тесты из этого контейнера будут выполнены на обоих сторонах.
@Common
public class TestCommomSide {
// Все before/after методы не должны бросать исключения.
@BeforeClass
static void beforeClass() {
// Статично импортированный LOG из ModInfo.
LOG.info("Выполнен на обоих сторонах перед всеми тестами этого класса");
}
@Test
void test$common() {
LOG.info("Выполнен на обоих сторонах");
}
}
// Тесты из этого контейнера будут выполнены только на стороне сервера.
@Server(when = LoadState.INITIALIZATION) // Тесты будут выполнены в конце (после всех модов) файлы инициализации.
public class TestServerSide {
@WorldShadow
World overworld; // Геттер для обычного мира (с id 0).
@AfterEach
void afterEach() {
LOG.info("Выполнен после каждого теста этого класса на строне сервера");
}
@Test
void test$server() {
LOG.info("Выполнен на стороне сервера");
}
}
Написание тестов
Каждый тест может вернуть один из трех результатов:
SUCCESS
- выброшено ожидаемое исключение (если представлено параметромexpected
в@Test
) и нет других исключений.FAILED
- ожидаемое исключение не выброшено (если представлено параметромexpected
в@Test
) или было выброшено исключение типаAssertionError
.ERROR
- просто ошибка в движке, например неправильный синтаксис тестов. В случае возникновения роняет игру.
Обратите внимание: Для того чтобы не бросать AssertionError исключение вручную, вы можете использовать вспомогательный класс
Assert
из пакета API. Вы также можете использовать одноименный класс из JUnit или любой другой класс упрощающий бросок AssertionError
из других фреймворков.Примеры
Java:
@Test(expected = ClassNotFoundException.class)
void test$clientClassOnServer() throws ClassNotFoundException {
// Тест приведет к ClassNotFoundException на стороне сервера и провалит тест.
// Все неожидаемые исключения будут обернуты в AssertionError.
Class.forName("net.minecraft.client.Minecraft");
// Используйте деобфусцированные имена без всяких проблем.
// Вы же не будете запускать движок на продакшене, не так ли?
}
@Test
void test$isServerSide() {
// Провалит тест, если условие вернет false.
Assert.isTrue(FMLCommonHandler.instance().getSide().isServer(), "Не сервер");
}
@WorldShadow // Только для серверных контейнеров тестов.
World overworld;
@Test
void test$shadowWorld() {
// Вы можете использовать несколько условий за раз.
Assert.isTrue(overworld != null, "Мир не найден");
Assert.isEquals(overworld.provider.dimensionId, 0, "Не обычный мир");
}
Запуск тестов
Для запуска тестов вы должны выполнить
testClient
и/или testServer
в gradle.Конфигурация
Настройки движка
Вы можете менять следующие настройки движка с помощью переменных среды и системных свойств JVM (
-Dkey=value
):Параметр | Описание | Значение по умолчанию |
---|---|---|
MCIGT_STOP_AFTER_SUCCESS или MCInGameTester.stopAfterSuccess | Останавливает игру после удачного выполнения тестов. | true |
MCIGT_FORCED_GAME_STOP_STATE или MCInGameTester.forcedGameStopState | Переопределените LoadState после которого игра будет остановлена. Все тесты у которых значение параметра when стоит после переопределенного будут проигнорированы. | Используется максимальное значение из представленных параметром when |
MCIGT_STOP_NO_TESTS или MCInGameTester.stopNoTests | Останавливает игру, если нет тестов. | true |
MCIGT_STOP_FIRST_FAIL или MCInGameTester.stopFirstFail | Останавливает игру при первом проваленном тесте. | false |
MCIGT_HALT_EXIT или MCInGameTester.haltExit | Использовать System.halt вместо System.exit для остановки игры. | false |
Обратите внимание: Значение из переменной среды приоритетнее системного свойства JVM.
Сборка
Вы можете настраивать задачи
testClient
/testServer
также как и runClient
/runServer
(наследники JavaExec), но они имеют дополнительные параметры:
Gradle (Groovy):
testServer { // или testClient
// Пересоздает рабочий каталог перед запуском.
clearWorkingDirBeforeLaunch = true
// Автоматическое принятие eula для сервера.
eula = true // (false у testClient)
// Использовать специальный файл конфигурации Log4J (mcingametester.xml) отобрадающий только ModInfo#LOG.
logOnlyTests = true
// Копирует маппинги в `../conf` от рабочей директории. Это может быть полезно
// если вы используете CodeChickenLib в проекте: Версия из репозитория GregTech
// умеет находить файлы маппингов автоматически, вместо того чтобы открывать
// файловый проводник на Java Swing.
copyMappingsLocally = true
// В дополнение, testServer имеет программный аргумент "nogui" по умолчанию.
}
Расширение скрипта сборки также может сделать
testClient
и testServer
зависимостями test
если переменная среды CI
равна true
, но клиент еще требует и переменную MCIGT_HAS_DISPLAY
с таким же значением. Это требуется потому что операционная система может быть без монитора, что приведет к падению клиента игры. В таком случае используйте любую библиотеку фейкового дисплея, например XVFB.CI
GitHub actions
Этот action запустит все тесты (
test
с testClient
и testServer
как зависимости) во время любого push'а или pull request'а.
Код:
name: Run gradle tests
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- name: Set up JDK 8
uses: actions/[email protected]
with:
java-version: '8'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run all tests headless
uses: GabrielBB/[email protected]
with:
run: ./gradlew test
env:
MCIGT_HAS_DISPLAY: true
JitCI
Спасибо команде jitpack.io за добавление XVFB в их докер контейнеры по моей просьбе.
Вот мои отличающиеся от стандартных настройки на примере этого проекта:
- Переменная стреды
MCIGT_HAS_DISPLAY
которая равнаtrue
. - Заменена команда тестирования на
xvfb-run -e /dev/stdout -s "-screen 0 1280x1024x24 -ac -nolisten tcp -nolisten unix" -a ./gradlew test
.
cache('http')
или что-то подобное.Обратите внимание: В
jitpack.yml
я использую следующиую конфигурацию с выключенным CI (но вы можете попробовать XVFB и тут):
Код:
jdk:
- openjdk8
install:
- ./gradlew build publishToMavenLocal
env:
CI: false
Реализованные и планируемые функции
"Движок".Задачи запуска.Интеграция для сервисов CI.- Настоящий плагин для gradle.
- JUnit реализация.
- JUnit-подобная реализация репортов.
- Портирование на другие версии игры, у которых нет альтернативных фреймворков.
- Реализовать встроенный фейковый дисплей или замокать всю графику.
Постскриптум
- Не до конца уверен с выбором категории ресурса, но раз "движок" является модом...
- Я изначально писал это в markdown и на английском для README, поэтому могут быть странные речевые обороты, ошибки и опечатки в тексте, не стесняйтесь исправлять меня