Hero background image
Как запускать автоматические тесты для своих игр с помощью Unity Test Framework

При разработке игр ручное тестирование может быстро стать повторяющимся и подверженным ошибкам. Вы когда-нибудь оказывались в одном из этих кажущихся бесконечными циклов тестирования, когда работали над новой функцией или пытались исправить ошибку?

Автоматизировав тестирование кода, вы сможете уделять больше времени творческой разработке игр и меньше - повторяющимся (но важным) задачам QA, которые гарантируют, что добавление, удаление или изменение кода не сломает ваш проект.

Unity помогает создавать, управлять и запускать автоматизированные тесты для ваших игр с помощью Unity Test Framework.

Тестирование двумя способами в Unity Test Framework

Unity Test Framework (UTF) позволяет тестировать код проекта как в режиме редактирования, так и в режиме воспроизведения. Вы также можете нацелить тестовый код на различные платформы, такие как standalone, iOS или Android.

UTF устанавливается путем добавления его в проект с помощью менеджера пакетов.

Под капотом UTF интегрируется с NUnit, которая является известной библиотекой тестирования с открытым исходным кодом для языков .NET.

Есть две основные категории тестов, которые можно написать с помощью UTF: режим редактирования и режим воспроизведения:

Тесты в режиме редактирования запускаются в редакторе Unity и имеют доступ как к редактору, так и к коду игры. Это означает, что вы можете тестировать свои пользовательские расширения редактора или использовать тесты для изменения настроек в редакторе и перехода в режим воспроизведения, что удобно для настройки значений инспектора и последующего запуска автоматизированных тестов с множеством различных настроек.

Тесты в режиме игры позволяют проверить код игры во время выполнения. Тесты обычно запускаются как корутины с помощью атрибута [UnityTest]. Это позволяет тестировать код, который может выполняться на нескольких фреймах. По умолчанию тесты режима Play запускаются в редакторе, но вы также можете запустить их в отдельной сборке плеера для различных целевых платформ.

автоматизированные тесты
Пакет Third Person Character Controller доступен в Unity Asset Store
Как тестировать Unity Test Framework

Чтобы следовать этому примеру, вам нужно установить пакет Starter Assets - Third Person Character Controller из магазина Unity Asset Store и импортировать его в новый проект.

автоматизированные тесты
Файл manifest.json
Настройка тестового фреймворка Unity

Установите UTF через Window > Package Manager. Найдите Test Framework в реестре Unity в менеджере пакетов. Убедитесь, что выбрали версию 1.3.3 (последняя версия на момент написания статьи).

После установки UTF откройте файл Packages/manifest.json с помощью текстового редактора и добавьте секцию testables после зависимостей, как показано ниже:

,
"testables": [
"com.unity.inputsystem"
]

Сохраните файл. Это пригодится позже, когда вам понадобится ссылаться на сборку Unity.InputSystem.TestFramework для тестирования и эмуляции ввода игрока.

Вернитесь в редактор и позвольте новой версии установиться.

автоматизированные тесты
Ссылки на определения сборок в инспекторе для контроллера персонажа от третьего лица
Определения тестовых сборок

Нажмите Window > General > Test Runner, чтобы открыть окно редактора Test Runner.

В этой части руководства мы сосредоточимся на создании тестов режима Play. Вместо того чтобы использовать опции Create Test Assembly Folder в окне Test Runner, вы будете создавать их в окне Project.

Выделив корень папки Project Assets, щелкните правой кнопкой мыши и выберите Create > Testing > Tests Assembly Folder.

Добавляется папка проекта Tests, содержащая файл Tests.asmdef (определение сборки). Это необходимо для того, чтобы тесты ссылались на ваши игровые модули и зависимости.

На код контроллера символов будут ссылаться в тестах, и ему также потребуется определение сборки. Далее вы зададите несколько определений сборок и ссылок, чтобы облегчить тестирование между модулями.

Щелкните правой кнопкой мыши папку проекта Assets/StarterAssets/InputSystem и выберите Create > Assembly Definition. Назовите его как-нибудь описательно, например StarterAssetsInputSystem.

Выберите новый файл StarterAssetsInputSystem.asmdef и, используя инспектор, добавьте ссылку на определение сборки для Unity.InputSystem. Нажмите кнопку Применить.

Щелкните правой кнопкой мыши папку проекта Assets/StarterAssets/ThirdPersonController/Scripts и выберите Create > Assembly Definition. Назовите его как-нибудь описательно, например ThirdPersonControllerMain.

Как и в случае с предыдущим определением сборки, откройте ThirdPersonControllerMain в инспекторе и выберите ссылки для:

- Unity.InputSystem

- StarterAssetsInputSystem

Нажмите кнопку Применить.

автоматизированные тесты
Добавление ссылок на определения сборки
Добавление ссылок на определения сборки

Чтобы эмулировать части системы ввода, вам нужно будет ссылаться на нее в своих тестах. Кроме того, вам нужно будет сослаться на пространство имен StarterAssets в сборке, которую вы создадите для кода контроллера от третьего лица.

Откройте файл Tests.asmdef в Инспекторе и добавьте ссылку на следующие определения сборки:

- UnityEngine.TestRunner

- UnityEditor.TestRunner

- Unity.InputSystem

- Unity.InputSystem.TestFramework

- ThirdPersonControllerMain

Нажмите кнопку Применить.

Окно «Настройки сборки»
Окно «Настройки сборки»
Ваш первый тест

В первом тесте мы рассмотрим основы загрузки и перемещения главного героя из пакета Third Person Controller.

Начните с установки нового проекта с простой сцены тестового окружения и ресурса Prefab персонажа для работы.

Откройте сцену с именем Assets/StarterAssets/ThirdPersonController/Scenes/Playground.unity и сохраните ее копию с помощью меню File > Save As по этому новому пути: Assets/Scenes/SimpleTesting.unity

Если вы заметили розовые материалы в режиме просмотра игры, воспользуйтесь конвертером конвейера рендеринга, чтобы обновить материалы из встроенного конвейера рендеринга до универсального конвейера рендеринга (URP). Краткое описание см. в этой статье.

Создайте новую папку в папке Project Assets под названием Resources. Примечание: Имя папки "Resources" здесь важно для того, чтобы можно было использовать метод Unity Resources.Load().

Перетащите игровой объект PlayerArmature из представления Scene в новую папку Resources и при появлении запроса выберите создание оригинального префаба. Переименуйте префаб актива Character.

Это будет базовый персонаж Prefab, который будет использоваться в ваших тестах в дальнейшем.

Удалите игровой объект PlayerArmature из новой сцены SimpleTesting и сохраните изменения в сцене.

Для последнего шага начальной настройки теста перейдите в меню File > Build Settings и выберите Add Open Scenes, чтобы добавить сцену Scenes/SimpleTesting в настройки сборки.

Создание сценария тестирования на C#

Выберите папку Tests в папке Project Assets. Щелкните правой кнопкой мыши и выберите Create > Testing > C# Test Script.

Назовите новый сценарий CharacterTests. Откройте скрипт в IDE, чтобы рассмотреть его поближе.

Вместе с файлом начального класса поставляются два метода-заглушки, демонстрирующие некоторые основы тестирования.

Далее вы убедитесь, что тесты загружают "ориентированную на тестирование" игровую сцену. Это должна быть сцена, содержащая необходимый минимум для тестирования системы или компонента, на котором вы сосредоточились.

Обновите класс CharacterTests, добавив в него два новых оператора использования, и реализуйте класс InputTestFixture:

using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;

public class CharacterTests : InputTestFixture

Добавьте два приватных поля в верхнюю часть класса CharacterTests:

GameObject character = Resources.Load<GameObject>("Character");
Клавиатура клавиатуры;

В поле персонажа будет храниться ссылка на префаб персонажа, загруженный из папки Resources. Keyboard будет содержать ссылку на устройство ввода Keyboard, предоставляемое InputSystem.

Переопределите метод Setup() базового класса InputTestFixture, предоставив свой собственный в классе CharacterTests:

public override void Setup()
{
SceneManager.LoadScene("Scenes/SimpleTesting");
base.Setup();
клавиатура = InputSystem.AddDevice<Keyboard>();

var mouse = InputSystem.AddDevice<Mouse>();
Нажмите(mouse.rightButton);
Отпустить(mouse.rightButton);;
}

Метод Setup() запускает метод базового класса Setup(), а затем устанавливает ваш собственный класс CharacterTests, загружая тестовую сцену и инициализируя устройство ввода клавиатуры.

Ввод мыши добавлен исключительно для того, чтобы контроллер от третьего лица начал получать ввод от симулируемого/виртуального клавиатурного устройства. Это почти как действие "установить фокус".

В первом тесте вы создадите персонажа из Prefab и убедитесь, что он не является null. Добавьте следующий метод в свой тестовый класс:

[Тест]
public void TestPlayerInstantiation()
{
GameObject characterInstance = GameObject.Instantiate(character, Vector3.zero, Quaternion.identity);
Assert.That(characterInstance, !Is.Null);
}

Пока вы там, возможно, захотите почистить образцы шаблонов методов тестирования. Удалите методы CharacterTestsSimplePasses и CharacterTestsWithEnumeratorPasses.

тест пройден проверка
Зеленая галочка означает, что тест успешно пройден
Пройдите свой первый тест

Сохраните сценарий и вернитесь в окно Test Runner в редакторе. Выделите тест TestPlayerInstantiation и нажмите Run Selected.

Зеленая галочка означает, что тест пройден. Вы утверждаете, что персонаж может быть загружен из ресурсов, инстанцирован в тестовую сцену и не является нулевым в этот момент.

Вы могли заметить, что для этого теста была использована аннотация [Test] вместо аннотации [UnityTest]. Атрибут UnityTest позволяет корутинам запускать тесты на нескольких кадрах. В этом случае вам нужно просто инстанцировать символ и подтвердить, что он был загружен.

Как правило, в режиме редактирования следует использовать атрибут NUnit Test вместо атрибута UnityTest, если только вам не нужно выдать специальные инструкции, пропустить кадр или подождать определенное время в режиме воспроизведения.

Тестирование движения персонажей в игровом режиме
Тестирование движения персонажей в игровом режиме
Тесты на движение персонажа

Далее вы используете UnityTest, чтобы подтвердить, что нажатие клавиши контроллера "вперед" перемещает персонажа вперед.

Добавьте новый метод тестирования, представленный ниже, в класс CharacterTests.

Появились два новых вспомогательных метода тестирования: Press() и Release(). Они предоставляются базовым классом InputTestFixture и помогают эмулировать нажатие и отпускание элементов управления InputSystem.

Метод TestPlayerMoves() выполняет следующие действия:

Инстанцирует экземпляр персонажа из префаба персонажа в месте (X: 0, Y: 0, Z: 0)

Нажимает клавишу со стрелкой вверх на виртуальной клавиатуре в течение 1 секунды, затем отпускает ее

Подождите еще 1 секунду (чтобы персонаж замедлился и перестал двигаться).

Утверждает, что символ переместился в положение по оси Z более чем на 1,5 единицы.

Сохраните файл, вернитесь в Test Runner и запустите новый тест.

Скрипт здоровья игрока
Скрипт здоровья игрока
Проверка повреждений при падении

Далее вы протестируете пользовательский скрипт Monobehaviour, добавив простой компонент Player Health.

Создайте новый скрипт в разделе Assets/StarterAssets/ThirdPersonController/Scripts. Назовите его PlayerHealth.

Откройте скрипт в вашей IDE и замените его содержимое на код, приведенный ниже.

Здесь добавлено много нового кода. Если кратко, то этот скрипт определяет, находится ли персонаж игрока в состоянии падения. Если во время падения один раз удариться о землю, то здоровье персонажа уменьшится на 10 %.

Найдите префаб персонажа в разделе Assets/Resources. Откройте префаб и добавьте новый компонент сценария PlayerHealth.

Далее вы используете тестовую сцену, чтобы убедиться, что здоровье игрока падает после падения с уступа.

Используя атрибут [UnityTest], вы можете написать тест режима Play, который проверяет повреждения при падении. При падении более 0,2 секунды игрок получает 0,1f урона (эквивалент 10% максимального здоровья).

В сцене SimpleTesting вы увидите лестницу, ведущую на уступ. Это тестовая платформа для спавна персонажа и проверки скрипта PlayerHealth.

Снова откройте CharacterTests.cs и добавьте новый метод проверки с именем TestPlayerFallDamage:

[UnityTest]
public IEnumerator TestPlayerFallDamage()
{
// породите персонажа в достаточно высокой области тестовой сцены
GameObject characterInstance = GameObject.Instantiate(character, new Vector3(0f, 4f, 17.2f), Quaternion.identity);

// Получите ссылку на компонент PlayerHealth и подтвердите, что в данный момент у игрока полное здоровье (1f)
var characterHealth = characterInstance.GetComponent<PlayerHealth>();
Assert.That(characterHealth.Health, Is.EqualTo(1f));

// Сойдите с уступа и ждите падения.
Press(keyboard.upArrowKey);
yield return new WaitForSeconds(0.5f);
Release(keyboard.upArrowKey);
yield return new WaitForSeconds(2f);

// Утверждаем, что 1 очко здоровья было потеряно из-за повреждения при падении
Assert.That(characterHealth.Health, Is.EqualTo(0.9f));
}

Вам также нужно будет добавить ссылку using на пространство имен StarterAssets в самом верху файла класса:

используя StarterAssets;

Приведенный выше тест следует типичному шаблону arrange, act, assert (AAA), который часто встречается в тестировании:

Секция Arrange метода юнит-теста инициализирует объекты и устанавливает значение данных, передаваемых тестируемому методу.

Секция Act вызывает тестируемый метод с заданными параметрами. В данном случае вызов тестируемого метода происходит при взаимодействии с физикой, когда игрок ударяется о землю после падения.

Секция Assert проверяет, что действия тестируемого метода выполняются в соответствии с ожиданиями.

Test Runner - запуск тестов
Тест, позволяющий убедиться, что персонаж падает в игре так, как задумано, включая получение правильного количества урона
Запуск нового теста

Вернувшись в редактор, запустите новый тест. Запустив режим игры, вы увидите, как персонаж отходит от края, падает (превысив порог в 0,2 секунды для классификации падения) и получает урон после удара о землю.

Тесты не только проверяют, что изменения кода не нарушают функциональность, но и могут служить документацией или указателями, помогающими разработчикам задуматься о других аспектах игры при изменении настроек.

Как переключить тест на запуск в автономной сборке плеера
Как переключить тест на запуск в автономной сборке плеера
Запуск тестов в автономном проигрывателе
Автоматизация и CI

Если вы начали создавать набор тестов, следующим шагом будет их автоматический запуск после завершения сборки. Автоматизированные модульные и интеграционные тесты, запускаемые после сборки, полезны для выявления регрессий и ошибок как можно раньше. Они также могут работать как часть удаленной автоматизированной системы сборки в облаке.

Разделение сборки и запуска

Зачастую вам необходимо фиксировать результаты тестирования в пользовательском формате, чтобы ими можно было поделиться с широкой аудиторией. Чтобы получить результаты тестирования за пределами редактора Unity, вам нужно разделить процессы сборки и запуска.

Создайте новый скрипт в папке проекта Tests с именем SetupPlaymodeTestPlayer.

Класс SetupPlaymodeTestPlayer будет реализовывать интерфейс ITestPlayerBuildModifier. Вы будете использовать его, чтобы переопределить и "подцепить" метод ModifyOptions, который получает параметры игрока сборки и позволяет вам их изменить.

используя System.IO;
using UnityEditor;
using UnityEditor.TestTools;

[сборка: TestPlayerBuildModifier(typeof(SetupPlaymodeTestPlayer))]
public class SetupPlaymodeTestPlayer : ITestPlayerBuildModifier
{
public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
{
playerOptions.options &= ~(BuildOptions.AutoRunPlayer | BuildOptions.ConnectToHost);

var buildLocation = Path.GetFullPath("TestPlayers");
var fileName = Path.GetFileName(playerOptions.locationPathName);
if (!string.IsNullOrEmpty(fileName))
buildLocation = Path.Combine(buildLocation, fileName);
playerOptions.locationPathName = buildLocation;

return playerOptions;
}
}

Этот пользовательский скрипт модификатора Player Build делает следующее, когда тесты запускаются в режиме игры (Run Location: On Player):

Отключает автозапуск для встроенных игроков и пропускает вариант игрока, который пытается подключиться к хосту, на котором он запущен

Изменяет местоположение пути сборки на выделенный путь в проекте(TestPlayers).

Теперь вы можете ожидать, что сборки будут располагаться в папке TestPlayers после завершения сборки. На этом модификации сборки завершены, и связь между сборкой и запуском разорвана.

Далее вы внедрите отчетность о результатах. Это позволит вам записывать результаты тестирования в пользовательское место, готовое к автоматизированной генерации и публикации отчетов.

Создайте новый скрипт в папке проекта Tests с именем ResultSerializer (см. ниже). Этот класс будет использовать ссылку на сборку TestRunCallback и реализовывать интерфейс ITestRunCallback.

Данная реализация ITestRunCallback включает в себя настроенный метод RunFinished, который настраивает сборку плеера с тестами на запись результатов тестирования в XML-файл с именем testresults.xml.

Программный код
Результаты можно найти в файле testresults.xml, расположенном в папке Application.persistentDataPath вашей платформы.
Запуск теста после разделения сборки и выполнения

При объединении SetupPlaymodeTestPlayer.cs и ResultSerializer.cs процессы сборки и выполнения теперь разделены. В результате выполнения тестов результаты будут выведены в файл testresults.xml, расположенный в папке Application.persistentDataPath платформы плеера.

Чтобы использовать некоторые типы в этих классах крючков, вам нужно будет добавить дополнительную ссылку на Tests.asmdef. Обновите его, чтобы добавить ссылку на определение сборки UnityEditor.UI.EditorTests.

Запуск тестов в проигрывателе приведет к созданию сборки проигрывателя под вашим проектом в папке TestPlayers и файла testresults.xml в папке Application.persistentDataPath.

Обложки для электронных книг
Другие ресурсы по тестированию в Unity

Курс по Unity Test Framework

Пакет Test Framework включает в себя курс по тестированию с примерами упражнений, которые помогут вам узнать больше о тестировании с помощью Unity. Не забудьте захватить файлы проекта для курса с помощью менеджера пакетов.

Использование диспетчера пакетов > Пакеты: Unity Registry > Test Framework, найдите выпадающий список Samples и импортируйте упражнения курса.

Упражнения будут импортированы в ваш проект и расположены в разделе Assets/Samples/Test Framework. Каждый пример включает в себя папку с упражнениями, по которым вам предстоит работать, а также решение, с которым вы можете сравнить свою работу по ходу дела.

Проверяйте свой код с помощью UTF

В докладе Unite Copenhagen о UTF рассказывается более подробно и предлагаются другие интересные варианты использования кастомизации тестов. Обязательно посмотрите его, чтобы узнать, что еще возможно.

Отладка в Unity

Ускорьте процесс отладки в Unity с помощью статей о:

- Microsoft Visual Studio 2022

- Код Microsoft Visual Studio

Передовые технические электронные книги

В Unity есть ряд руководств, помогающих профессиональным разработчикам оптимизировать игровой код. Создайте руководство по стилю C#: Пишите более чистый код, который масштабируется собраны советы экспертов отрасли о том, как создать руководство по стилю кода, чтобы помочь вашей команде разработать чистую, читаемую и масштабируемую кодовую базу.

Еще одним популярным руководством среди наших пользователей является 70+ советов по повышению производительности с помощью Unity. В нем собраны советы по экономии времени, которые помогут вам улучшить повседневный рабочий процесс с Unity 2020 LTS, включая советы, которые могли пропустить даже опытные разработчики.

Документация

Изучите новейший API TestRunner, узнайте о других пользовательских атрибутах UTF и откройте для себя другие жизненные циклы, которые можно использовать в документации UTF.

Найдите все передовые электронные книги и статьи Unity в хабе "Лучшие практики Unity".

Понравился ли вам этот контент?