gdtest

Writing Your First Test

Learn the shape of a gdtest test file and the hooks you will use most often.

Smallest useful test

A gdtest file is a normal GDScript file that:

  • extends GdTest
  • contains one or more test_* functions
# test/test_battle.gd
extends GdTest

func test_basic_hit() -> void:
    assert_eq(Battle.calc_damage(10, 3), 7)

Run it with:

gdtest test/test_battle.gd

Why use gdtest assertions?

GDScript's built-in assert() stops the worker immediately on failure.

assert(Battle.calc_damage(10, 3) == 7)

gdtest assertions report the failure and continue with the rest of the suite.

assert_eq(Battle.calc_damage(10, 3), 7)

They also show expected and actual values in the failure output.

A basic file

extends GdTest

func test_basic_hit() -> void:
    assert_eq(Battle.calc_damage(10, 3), 7)

func test_crit_doubles_damage() -> void:
    assert_eq(Battle.calc_damage(10, 3, true), 14)

func test_no_negative_damage() -> void:
    assert_eq(Battle.calc_damage(1, 999), 0)

Each test_* function runs in a fresh instance.

Shared setup

Use before_each and after_each when tests need the same object state:

extends GdTest

var attacker: Character

func before_each() -> void:
    attacker = Character.new()
    attacker.strength = 10

func after_each() -> void:
    attacker.free()

func test_base_damage() -> void:
    assert_eq(Battle.calc_damage(attacker, 3), 7)

Use before_all and after_all for one-time setup per file.

Grouping

Inner classes named describe_* become sub-suites:

extends GdTest

class describe_critical_hit extends GdTest:
    func test_doubles_damage() -> void:
        assert_eq(Battle.calc_damage(10, 3, true), 14)

Inner classes must also extend GdTest.

Skipping work in progress

func test_not_ready() -> void:
    pending("not implemented yet")

To run one test while debugging:

gdtest --filter "test_basic_hit"

Next: Assertions Reference