Хабрахабр

[Перевод] Введение в юнит-тестирование в Unity

image

Вам любопытно, как работает юнит-тестирование в Unity? Не знаете, что такое юнит-тестирование в целом? Если вы ответили положительно на эти вопросы, то данный туториал будет вам полезен. Из него вы узнаете о юнит-тестировании следующее:

  • Что это такое
  • Его польза
  • Достоинства и недостатки
  • Как оно работает в Unity при использовании Test Runner
  • Как писать и выполнять юнит-тесты, которые будут проходить проверку

Если вы новичок в Unity, то изучите сначала другие туториалы по этому движку. Примечание: в этом туториале предполагается, что вы знакомы с языком C# и основами разработки в Unity.

Что такое юнит-тест (Unit Test)?

Прежде чем углубляться в код, важно получить чёткое понимание того, что такое юнит-тестирование. Если говорить просто, то юнит-тестирование — это тестирование… юнитов.

Состав «юнита» может варьироваться, но важно помнить, что юнит-тестирование должно тестировать ровно один «элемент» за раз.
Юнит-тесты необходимо создавать для проверки того, что небольшой логический фрагмент кода в конкретном сценарии выполняется именно так, как вы ожидаете. Юнит-тест (в идеале) предназначен для тестирования отдельного «юнита» кода. Это может быть сложно понять, прежде чем вы начнёте писать собственные юнит-тесты, поэтому давайте рассмотрим пример:

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

public string name = ""
public void UpdateNameWithCharacter(char: character)
// 2 if (name.Length > 10) { return; } // 3 name += character;
}

Что здесь происходит:

  1. Если символ не является буквой, то код выполняет предварительный выход из функции и не добавляет символ в строку.
  2. Если длина имени составляет десять или более символов, то код не позволяет пользователю добавить ещё один символ.
  3. Если эти две проверки пройдены, то код добавляет в конец имени символ.

Этот юнит можно протестировать, потому что он представляет собой «модуль» выполняемой работы. Юнит-тесты принудительно выполняют логику метода.

Пример юнит-тестов

Как нам написать юнит-тесты для метода UpdateNameWithCharacter?

Прежде чем мы начнём реализовывать эти юнит-тесты, нужно тщательно продумать, что делают эти тесты, и придумать для них названия.

Из названий должно быть понятно, что они проверяют: Посмотрите на показанные ниже примеры названий юнит-тестов.

UpdateNameDoesntAllowCharacterAddingToNameIfNameIsTenOrMoreCharactersInLength

UpdateNameAllowsLettersToBeAddedToName

UpdateNameDoesntAllowNonLettersToBeAddedToName

Эти названия тестов могут показаться слишком длинными и подробными, но это нам на пользу. Из этих названий тестовых методов видно, что мы действительно проверяем, выполняется ли «юнит» работы методом UpdateNameWithCharacter.

Комплекс тестов содержит все юнит-тесты, относящиеся к логической группе функционала (например, «юнит-тесты боя»). Каждый написанный вами юнит-тест является частью комплекса тестов. Если любой тест из комплекта не проходит проверку, то её не проходит весь комплект тестов.

Запуск игры

Откройте Crashteroids Starter project (скачать его можно отсюда), а затем откройте сцену Game из папки Assets / RW / Scenes.

Нажмите на Play, чтобы запустить Crashteroids, а затем нажмите на кнопку Start Game. Перемещайте космический корабль стрелками влево и вправо на клавиатуре.

Если луч попадёт в астероид, то счёт увеличится на единицу. Для выстрела лазерным лучом нажимайте пробел. Если астероид сталкивается с кораблём, то корабль взрывается и игра завершается (с возможностью начать заново).

Попробуйте немного поиграть и убедиться, что после столкновения астероида с кораблём появляется надпись Game Over.

Начинаем работу с Unity Test Runner

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

Test Runner позволяет выполнять тесты и проверять, проходятся ли они успешно. Чтобы писать тесты, сначала нужно узнать о Unity Test Runner. Чтобы открыть Unity Test Runner, выберите Window ▸ General ▸ Test Runner.

После того, как в новом окне откроется Test Runner, можно будет упростить себе жизнь, нажав на окно Test Runner и перетащив его на место рядом с окном Scene.

Подготовка NUnit и папок тестов

Test Runner — это предоставляемая Unity функция юнит-тестирования, но она использует фреймворк NUnit. Когда вы начнёте работать с юнит-тестами серьёзнее, то рекомендую изучить wiki по NUnit, чтобы узнать больше. Про всё необходимое на первое время будет рассказано в этой статье.

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

Посмотрите на окно Test Runner и убедитесь, что выбран PlayMode. В окне Project выберите папку RW.

Вы увидите, что в папке RW появится новая папка. Нажмите кнопку с названием Create PlayMode Test Assembly Folder. Нас устроит стандартное название Tests, поэтому можно просто нажать Enter.

Возможно, вам интересно, что это за две разные вкладки внутри Test Runner.

Тесты вкладки EditMode выполняются вне режима Play, что удобно для тестирования таких вещей, как пользовательские behaviors в Inspector. Вкладка PlayMode используется для тестов, выполняемых в режиме Play (когда игра выполняется в реальном времени).

Но когда освоитесь, можете попробовать поэкспериментировать и с тестированием в EditMode. В этом туториале мы будем рассматривать тесты PlayMode. При работе с Test Runner в этом туториале всегда проверяйте, что выбрана вкладка PlayMode.

Что находится в комплекте тестов?

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

Файл класса, содержащий юнит-тесты, называется комплектом тестов (test suite). Test Runner обходит все файлы классов тестов и выполняет юнит-тесты из них.

Мы должны разделять код тестов на отдельные логичные комплекты (например, комплект тестов для физики и отдельный комплект для боя). В комплекте тестов мы логически подразделяем наши тесты. В этом туториале нам понадобится только один комплект тестов, и настало время его создать.

Подготовка тестовой сборки и комплекта тестов

Выберите папку Tests и в окне Test Runner нажмите на кнопку Create Test Script in current folder. Назовите новый файл TestSuite.

Кроме нового файла C# движок Unity также создаёт ещё один файл под названием Tests.asmdef. Это файл определения сборки (assembly definition file), который используется для того, чтобы показать Unity, где находятся зависимости файла теста. Это нужно, потому что код готового приложения содержится отдельно от тестового кода.

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

Нажмите на папку Scripts, чтобы выбрать её. Чтобы код тестов имел доступ к классам игры, мы создадим сборку кода классов и зададим ссылку в сборке Tests. Нажмите правой клавишей на эту папку и выберите Create ▸ Assembly Definition.

Назовите файл GameAssembly.

Нажмите на папку Tests, а затем на файл определения сборки Tests. В Inspector нажмите на кнопку плюс под заголовком Assembly Definition References.

Вы увидите поле Missing Reference. Нажмите на точку рядом с этим полем, чтобы открыть окно выбора. Выберите файл GameAssembly.

Вы должны увидеть файл сборки GameAssembly в разделе ссылок. Нажмите на кнопку Apply, чтобы сохранить эти изменения.

Если вы не выполните эти действия, то не сможете ссылаться на файлы классов игры внутри файлов юнит-тестов. Разобравшись с этим, можно приступать к коду.

Пишем первый юнит-тест

Дважды нажмите на скрипт TestSuite, чтобы открыть его в редакторе кода. Замените весь код на такой:

using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections; public class TestSuite
{ }

Какие тесты нам нужно написать? Честно говоря, даже в такой крошечной игре, как Crashteroids, можно написать довольно много тестов для проверки того, что всё работает как надо. В этом туториале мы ограничимся только ключевыми областями: распознаванием коллизий и базовой игровой механикой.

Примечание: когда дело доходит до написания юнит-тестов продукта уровня продакшена, стоит уделить достаточно времени, чтобы учесть все граничные случаи, которые нужно протестировать во всех областях кода.

В качестве первого теста неплохо будет проверить, действительно ли астероиды движутся вниз. Им будет сложно столкнуться с кораблём, если они будут от него отдаляться! Добавим в скрипт TestSuite следующий метод и частную переменную:

private Game game; // 1
[UnityTest]
public IEnumerator AsteroidsMoveDown()
{ // 2 GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); // 3 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); // 4 float initialYPos = asteroid.transform.position.y; // 5 yield return new WaitForSeconds(0.1f); // 6 Assert.Less(asteroid.transform.position.y, initialYPos); // 7 Object.Destroy(game.gameObject);
}

Здесь всего несколько строк кода, но они выполняют множество действий. Так что давайте остановимся и разберёмся с каждой частью:

  1. Это атрибут. Атрибуты определяют особые поведения компилятора. Данный атрибут сообщает компилятору Unity, что код является юнит-тестом. Благодаря этому он отобразится в Test Runner при запуске тестов.
  2. Создаём экземпляр Game. Всё остальное вложено в game, поэтому когда мы создадим его, в нём будет находиться всё, что нужно тестировать. В среде продакшена скорее всего все элементы не будут находиться внутри одного префаба. Поэтому вам потребуется воссоздать все объекты, необходимые в сцене.
  3. Здесь мы создаём астероид, чтобы можно было следить за тем, двигается ли он. Метод SpawnAsteroid возвращает экземпляр созданного астероида. Компонент Asteroid имеет метод Move (если вам любопытно, как работает движение, то можете взглянуть на скрипт Asteroid внутри RW / Scripts).
  4. Отслеживание исходной позиции необходимо для того, чтобы убедиться, что астероид сместился вниз.
  5. Все юнит-тесты Unity являются корутинами, поэтому нужно добавить мягкий возврат. Также мы добавляем шаг времени в 0,1 секунды, чтобы симулировать течение времени, за которое астероид должен был двигаться вниз. Если вам не нужно симулировать шаг времени, то можно вернуть null.
  6. Это этап утверждения (assertion), на котором мы утверждаем, что позиция астероида меньше исходной позиции (то есть он сдвинулся вниз). Понимание утверждений — важная часть юнит-тестирования, и NUnit предоставляет различные методы утверждений. Прохождение или непрохождение теста определяется этой строкой.
  7. Разумеется, никто не наругает вас за оставленный после завершения тестов беспорядок, но другие тесты могут из-за него закончиться неудачно. Всегда важно подчищать (удалять или сбрасывать) код после юнит-теста, чтобы при запуске следующего юнит-теста не оставалось артефактов, которые могли бы повлиять на этот тест. Нам достаточно просто удалить игровой объект, потому что для каждого теста мы создаём полностью новый экземпляр game.

Прохождение тестов

Отлично, вы написали свой первый юнит-тест, но как узнать, что он работает? Разумеется, с помощью Test Runner! В окне Test Runner раскройте все строки со стрелками. Вы должны увидеть тест AsteroidsMoveDown в списке с серыми кружками:

Серый кружок означает, что тест пока не выполнялся. Если тест был запущен и пройден, то рядом показывается зелёная стрелка. Если тест завершился с ошибкой, то рядом с ним будет отображён красный X. Запустим тест, нажав на кнопку RunAll.

При этом создастся временная сцена и будет запущен тест. После завершения вы должны увидеть, что тест пройден.

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

Если вам любопытно, как работает тестируемая вами логика, то изучите код в папке RW / Scripts. Примечание: прежде чем начать писать собственные юнит-тесты, вам нужно понять реализацию, которую вы тестируете.

Использование интеграционных тестов

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

«Модуль» — это ещё один нечёткий термин. Интеграционные тесты — это тесты, проверяющие как работают «модули» кода совместно. когда игрок по-настоящему играет в игру). Важное отличие заключается в том, что интеграционные тесты должны тестировать работу ПО в настоящем продакшене (т.е.

Допустим, вы сделали игру с боями, где игрок убивает монстров. Можно создать интеграционный тест, чтобы убедиться, что когда игрок убивает 100 врагов, открывается достижение («ачивка»).

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

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

В этом туториале мы не будем изучать интеграционные тесты, но это должно показать разницу между «юнитом» работы (и то, зачем её юнит-тестируют) и «модулем» кода (и то, зачем его тестируют интеграционно).

Добавление теста в комплект тестов

Следующий тест будет тестировать конец игры, когда корабль сталкивается с астероидом. Открыв в редакторе кода TestSuite, добавьте под первым юнит-тестом показанный ниже тест и сохраните файл:

[UnityTest]
public IEnumerator GameOverOccursOnAsteroidCollision()
{ GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); Game game = gameGameObject.GetComponent<Game>(); GameObject asteroid = game.GetSpawner().SpawnAsteroid(); //1 asteroid.transform.position = game.GetShip().transform.position; //2 yield return new WaitForSeconds(0.1f); //3 Assert.True(game.isGameOver); Object.Destroy(game.gameObject);
}

Мы уже видели бОльшую часть этого кода в предыдущем тесте, но здесь есть некоторые отличия:

  1. Мы вынуждаем астероид и корабль столкнуться, явно задавая астероиду ту же позицию, что и кораблю. Это создаст коллизию их хитбоксов и приведёт к концу игры. Если вам любопытно, как работает этот код, то взгляните на файлы Ship, Game, и Asteroid в папке Scripts.
  2. Шаг времени необходим, чтобы сработало событие Collision физического движка, поэтому возвращается задержка в 0,1 секунды.
  3. Это утверждение истины, и оно проверяет, что флаг gameOver в скрипте Game принимает значение true. Флаг принимает значение true во время работы игры, когда уничтожается корабль, то есть мы тестируем, чтобы убедиться, что ему присваивается значение true после уничтожения корабля.

Вернитесь в окно Test Runner и вы увидите, что там появился новый юнит-тест.

На этот раз мы запустим вместо всего комплекта тестов только этот. Нажмите на GameOverOccursOnAsteroidCollision, а затем на кнопку Run Selected.

И вуаля, мы прошли ещё один тест.

Этапы настройки и разрушения

Вы могли заметить, что в двух наших тестах есть повторяющийся код: там, где создаётся игровой объект Game и где задаётся ссылка на скрипт Game:

GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game"));
game = gameGameObject.GetComponent<Game>();

Также вы заметите, что повтор есть в уничтожении игрового объекта Game:

Object.Destroy(game.gameObject);

При тестировании такое случается очень часто. Когда дело доходит до запуска юнит-тестов, то на самом деле существует два этапа: этап «настройки» (Setup) и этап «разрушения» (Tear Down).

Весь код внутри метода Setup будет выполняться до юнит-теста (в этом комплекте), а весь код внутри метода Tear Down будет выполняться после юнит-теста (в этом комплекте).

Откройте редактор кода и добавьте следующий код в начало файла TestSuite, прямо перед первым атрибутом [UnityTest]: Настало время упростить нашу жизнь, переместив код setup и tear down в специальные методы.

[SetUp]
public void Setup()
{ GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>();
}

Атрибут SetUp указывает, что этот метод вызывается до выполнения каждого теста.

Затем добавим следующий метод и сохраним файл:

[TearDown]
public void Teardown()
{ Object.Destroy(game.gameObject);
}

Атрибут TearDown указывает, что этот метод вызывается после выполнения каждого теста.

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

public class TestSuite
{ private Game game; [SetUp] public void Setup() { GameObject gameGameObject = MonoBehaviour.Instantiate(Resources.Load<GameObject>("Prefabs/Game")); game = gameGameObject.GetComponent<Game>(); } [TearDown] public void Teardown() { Object.Destroy(game.gameObject); } [UnityTest] public IEnumerator AsteroidsMoveDown() { GameObject asteroid = game.GetSpawner().SpawnAsteroid(); float initialYPos = asteroid.transform.position.y; yield return new WaitForSeconds(0.1f); Assert.Less(asteroid.transform.position.y, initialYPos); } [UnityTest] public IEnumerator GameOverOccursOnAsteroidCollision() { GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = game.GetShip().transform.position; yield return new WaitForSeconds(0.1f); Assert.True(game.isGameOver); }
}

Тестируем Game Over и стрельбу лазером

Подготовив упрощающие нашу жизнь методы настройки и разрушения, можно приступить к добавлению новых тестов, в которых они используются. Следующий тест должен проверять, что когда игрок нажимает New Game, значение gameOver bool не равно true. Добавьте такой тест в конец файла и сохраните его:

[UnityTest]
public IEnumerator NewGameRestartsGame()
{ //1 game.isGameOver = true; game.NewGame(); //2 Assert.False(game.isGameOver); yield return null;
}

Это уже должно казаться вам привычным, но стоит упомянуть следующее:

  1. Эта часть кода подготавливает этот тест к тому, что булев флаг gameOver должен иметь значение true. При вызове метода NewGame он должен снова присваивать флагу значение false.
  2. Здесь мы утверждаем, что bool isGameOver равен false, что должно быть справедливо при вызове новой игры.

Вернитесь в Test Runner и вы должны увидеть что там появился новый тест NewGameRestartsGame. Запустите этот тест, как мы делали это ранее, и вы увидите, что он успешно выполняется:

Утверждение о движении лазерного луча

Следующим тестом нужно добавить тест того, что выстреливаемый кораблём лазерный луч летит вверх (аналогично первому написанному нами юнит-тесту). Откройте в редакторе файл TestSuite. Добавьте следующий метод и сохраните файл:

[UnityTest]
public IEnumerator LaserMovesUp()
{ // 1 GameObject laser = game.GetShip().SpawnLaser(); // 2 float initialYPos = laser.transform.position.y; yield return new WaitForSeconds(0.1f); // 3 Assert.Greater(laser.transform.position.y, initialYPos);
}

Вот что делает этот код:

  1. Получает ссылку на созданный лазерный луч, испущенный из корабля.
  2. Исходная позиция записывается, чтобы мы могли проверить, что он движется вверх.
  3. Это утверждение соответствует утверждению из юнит-теста AsteroidsMoveDown, только теперь мы утверждаем, что значение больше (то есть лазер движется вверх).

Сохраните файл и вернитесь в Test Runner. Запустите тест LaserMovesUp и понаблюдайте за его прохождением:

Теперь вы уже должны начать разбираться, как всё устроено, поэтому настало время добавить последние два теста и завершить туториал.

Проверка того, что лазер уничтожает астероиды

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

[UnityTest]
public IEnumerator LaserDestroysAsteroid()
{ // 1 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = Vector3.zero; GameObject laser = game.GetShip().SpawnLaser(); laser.transform.position = Vector3.zero; yield return new WaitForSeconds(0.1f); // 2 UnityEngine.Assertions.Assert.IsNull(asteroid);
}

Вот как это работает:

  1. Мы создаём астероид и лазерный луч, и присваиваем им одинаковую позицию для срабатывания коллизии.
  2. Это особая проверка с важным отличием. Видите, что мы явным образом используем для этого теста UnityEngine.Assertions? Так происходит потому, что в Unity есть особый класс Null, отличающийся от «обычного» класса Null. Утверждение фреймворка NUnit Assert.IsNull() не будет работать в проверках Unity на null. При проверках на null в Unity, нужно явным образом использовать UnityEngine.Assertions.Assert, а не Assert из NUnit.

Вернитесь в Test Runner и запустите новый тест. Вы увидите радующий нас зелёный значок.

Тестировать или не тестировать — вот в чём вопрос

Решение придерживаться юнит-тестов — непростое решение, и к нему не стоит относиться легкомысленно. Однако преимущества тестов стоят вложенных усилий. Существует даже методология разработки, называющаяся разработкой через тестирование (Test Driven Development, TDD).

Сначала вы создаёте тесты, убеждаетесь, что программа их не проходит, а затем пишете только код, предназначенный для прохождения тестов. Работая в рамках TDD, вы пишете тесты до написания самой логики приложения. Пусть это очень отличающийся подход к кодингу, но он гарантирует, что вы пишете код пригодным для тестирования образом.

Но пока время писать собственные юнит-тесты, для которых нужна игра, которую мы вам и предоставили. Помните об этом, когда приступите к работе над следующим проектом.

Некоторые люди считают, что частные методы должны тестироваться только через использующие их общие методы. Примечание: нужно принять решение — тестировать только общие методы, или ещё и частные. С другой стороны, тестирование частных методов может быть проблематичным и требовать особых фреймворков или инструментов рефлексии. Это может сделать «юнит» кода, который нужно протестировать, довольно большим и может оказаться нежелательным. В этом туториале все методы сделаны общими, чтобы их проще было отслеживать, поэтому не берите его в качестве примера при написании кода для продакшена. Каждый из вариантов имеет свою плюсы и минусы, рассмотрение которых не входит в рамки данного туториала.

Тестирование может быть большим вложением усилий, поэтому стоит рассмотреть достоинства и недостатки добавления юнит-тестирования в ваш проект:

Достоинства юнит-тестирования

У юнит-тестирования есть множество важных плюсов, в том числе и такие:

  • Оно даёт уверенность, что метод ведёт себя так, как ожидалось.
  • Служит документацией для новых людей, изучающих кодовую базу (юнит-тесты отлично подходят для преподавания).
  • Заставляет вас писать код тестируемым образом.
  • Позволяет изолировать и устранять ошибки быстрее.
  • Не позволяет будущим обновлениям добавлять новые баги в старый работающий код (они называются регрессионными ошибками).

Недостатки юнит-тестирования

Однако у вас может и не быть времени или бюджета на юнит-тестирование. Вот его недостатки которые нужно учесть:

  • Написание тестов может занять больше времени, чем сам код.
  • Плохие или неточные тесты создают ложную уверенность.
  • Для правильной реализации нужно больше знаний.
  • Возможно, важные части кодовой базы нельзя будет покрыть тестами.
  • Некоторые фреймворки не позволяют с лёгкостью тестировать частные методы, что может усложнить юнит-тестирование.
  • Если тесты слишком хрупки (их слишком легко не пройти по ошибочным причинам), то на обслуживание может уйти много времени.
  • Юнит-тесты не отлавливают интеграционные ошибки.
  • UI тестировать сложно.
  • Неопытные разработчики могут тратить зря время на тестирование не тех аспектов.
  • Иногда тестирование элементов с внешними зависимостями или зависимостями времени выполнения может быть очень сложным.

Тестирование того, что при уничтожении астероидов увеличивается счёт

Настало время писать последний тест. Откройте редактор кода, добавьте показанный ниже код в конец файла TestSuite и сохраните его:

[UnityTest]
public IEnumerator DestroyedAsteroidRaisesScore()
{ // 1 GameObject asteroid = game.GetSpawner().SpawnAsteroid(); asteroid.transform.position = Vector3.zero; GameObject laser = game.GetShip().SpawnLaser(); laser.transform.position = Vector3.zero; yield return new WaitForSeconds(0.1f); // 2 Assert.AreEqual(game.score, 1);
}

Это важный тест, проверяющий, что когда игрок уничтожает астероид, счёт увеличивается. Вот из чего он состоит:

  1. Мы создаём астероид и лазерный луч, и помещаем их в одну позицию. Благодаря этому возникает коллизия, которая запускает увеличение счёта.
  2. Утверждение, что game.score теперь равно 1 (а не 0, как было в начале).

Сохраните код и вернитесь в Test Runner, чтобы запустить этот последний тест и проверить, проходит ли его игра:

Потрясающе! Все тесты пройдены.

Куда двигаться дальше?

В статье мы рассмотрели большой объём информации. Если вы хотите сравнить свою работу с финальным проектом, то посмотрите его в архиве, ссылка на который также указана в начале статьи.

Кроме того, вы написали шесть юнит-тестов, которые успешно прошёл код, и познакомились с некоторыми из плюсов и минусов юнит-тестирования. Из этого туториала вы узнали, что такое юнит-тесты и как писать их в Unity.

Тогда можно написать ещё множество тестов. Чувствуете себя уверенно? Подумайте над добавлением тестов для следующих сценариев: Изучите файлы классов игры и попробуйте написать юнит-тесты для других частей кода.

  • Каждый тип астероида при касании корабля приводит к концу игры.
  • Запуск новой игры обнуляет счёт.
  • Движение влево и вправо для корабля действует правильно.

Если вы хотите повысить уровень своих знаний о юнит-тестировании, то стоит изучить внедрение зависимостей и фреймворки для работы с mock-объектами. Это может сильно упростить настройку тестов.

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

И не стесняйтесь делиться своими мыслями и вопросами на форумах.

Успешного тестирования!

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть