paint-brush
Тестирование в Godot: как лично я к этому подхожук@dlowl
730 чтения
730 чтения

Тестирование в Godot: как лично я к этому подхожу

к D. Lowl4m2024/03/07
Read on Terminal Reader

Слишком долго; Читать

Godot Unit Test (GUT) можно установить из AssetLib в один клик. Он предоставляет класс, который мы можем расширить для наших тестовых сценариев. У него приятный пользовательский интерфейс с возможностью использовать отладчик Godot и запускать отдельные тесты. Его можно запустить из CLI и в CI (но этим я займусь позже)
featured image - Тестирование в Godot: как лично я к этому подхожу
D. Lowl HackerNoon profile picture

Чтобы сменить темп, я хотел бы сделать небольшой журнал разработки. Некоторое время назад я участвовал в геймджеме и сделал эту игру — Of Mice and Bad Choices — короткую головоломку, в которой вы раскладываете сыр по лабиринту, чтобы выманить мышей. Было весело, но явно были некоторые недостатки.


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


Итак, я думаю, что это хорошая возможность провести автоматическое тестирование в Godot.

Иллюстрация ожидаемого поведения: мышь должна отойти от голубого сыра.

Инструменты тестирования

Для Godot 4 доступно несколько фреймворков тестирования, но меня привлекла Godot Unit Test (GUT) . GUT довольно прост:


  • Его можно установить из AssetLib в один клик.


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


  • Он имеет приятный пользовательский интерфейс с возможностью использовать отладчик Godot и запускать отдельные тесты.


  • Его можно запустить из CLI и в CI (но этим я займусь позже).

Моя среда тестирования

В данном конкретном случае мне хотелось иметь возможность определять сложные сценарии так же, как я определяю уровни для игры – в редакторе движка, а не в коде (так тесты были бы ближе к реальности). Следовательно, я хочу сделать следующие вещи:


  • Создайте одну функцию-бегун, которая принимает карту и запускает тесты.


  • Имейте коллекцию карт, каждая из которых имеет набор сценариев (тестовых случаев) для выполнения.


  • Имейте возможность определять тестовые примеры методом перетаскивания: поместите мышь и установите, где она должна находиться за N ходов.


Итак, давайте развернем это.

Определения тестовых примеров

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

 extends Node2D class_name MouseTestCase @export var steps_left = 0 # How many steps to simulate @export var done = false @onready var mouse: Mouse = $Mouse @onready var expected_position = SnapUtils.get_tile_map_position($TestMarker.position)


Теперь мы можем выпустить это на сцену, и у нас все хорошо! Мы знаем, где начинается мышь, знаем, где она должна оказаться и через сколько шагов.

Дерево узлов для определения тестового примера

Вот как это выглядит на карте: проверяемая мышь - зеленый цвет, целевой маркер - красный.

Тестовые карты

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

Итоговая тестовая карта для механических испытаний на отталкивание.

Такое поведение довольно сложное, поэтому мы хотим охватить множество несколько разных случаев:

  • Мышка хочет отойти от нелюбимого сыра/


  • Мышь хочет сохранить направление движения (т.е. избегает поворотов).


  • Мышь предпочитает левые повороты правым и разворотам.


Результирующая карта, определяющая 12 тестовых примеров для покрытия этого поведения, показана выше (представьте, насколько утомительным может быть жесткое кодирование всех этих координат в коде).

Тестовый бегун

Единственное, что осталось сделать, это функцию запуска тестов. Функция должна:

  • Загрузите карту, которую мы определили выше.


  • Имитируйте шаги игры вперед, пока не будут выполнены все тестовые примеры.


  • На каждом шаге перебирайте все тестовые примеры и, если они выполнены, проверяйте, достигнута ли ожидаемая позиция.


Код довольно прост.

 func run_level_with_mouse_test_cases(map_path: String): var level = load(map_path) map.load_level(level) var cases = MouseTestCase.cast_all_cases(get_tree().get_nodes_in_group(MouseTestCase.MTC_GROUP_NAME)) while (cases.any(func(case): return not case.done)): map.move_mice() for case in cases: if not case.done: case.steps_left -= 1 if case.steps_left == 0: case.done = true assert_eq(case.get_mouse_position(), case.expected_position, case.get_parent().name+"/"+case.name)

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

Панель GUT, показывающая успешный тестовый запуск

Обсуждения

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


Как всегда, код доступен на GitHub: https://github.com/d-lowl/of-mice-and-bad-choices . Вы также можете просмотреть конкретный PR , посвященный тестированию. Что касается бонусных баллов, если кто-то сможет заставить их работать в CI, это было бы великолепно. Ваше здоровье.