Skip to content

Testing your game

The example below tests the game made in Your first game.

A system test is a Ruby script that plays your game: it holds keys, waits, checks what happened and can take screenshots. It runs the real game, frame by frame, at the same pace on any machine.

# tests/collect_coin_test.rb
SystemTest.define do |t|
  t.wait(30)                                   # frames
  t.hold(:w, frames: 60)                       # walk forward for a second
  t.wait_until(frames: 30) { Scene.find("Coin1").nil? }
  t.assert("the score counts the coin") { Scene.find("Player").get_component(Player).coins == 1 }
  t.screenshot("coin_collected")
end

Run it with redgem-test, the program that runs tests (it is built next to redgem):

./build/src/redgem-test my_game tests/collect_coin_test.rb --screenshots shots

The command exits with an error when an assertion fails or a script raises, so it fits in any automated check. Add --headless to run without opening a window (no screenshots then); redgem-test --help lists every option.

redgem-test is a development tool. It is not part of a built game: the program your players receive (what redgem build and the editor's Build make) contains no test code at all, and SystemTest and the rest of what tests are written with do not exist in the Ruby your game runs in. They are only there while redgem-test plays it.

What a test can do

Call Effect
t.wait(frames) Lets the game run
t.hold(:w, :a, frames: 60) Holds keys for that long
t.press(:space) Taps a key
t.move_mouse(x, y), t.click(x, y) Moves the mouse; clicks there with the left button
t.type("Ana") Types a text (what Input.text and UI.text_field read)
t.tilt(:left_y, -1.0, frames: 60) Pushes a gamepad stick (or pulls a trigger) for that long
t.press_button(:a), t.hold_button(:a, frames: 30) Taps or holds gamepad buttons
t.hold_until(:w, frames: 300) { ... } Holds keys until the block is true
t.wait_until(frames: 120) { ... } Waits until the block is true, or fails
t.assert("message") { ... } Fails the test when the block is false
t.screenshot("name") Saves a picture of the next frame as name.png in the screenshots folder (--screenshots <dir>, screenshots unless said)

Inside the blocks you have the whole scripting API, so a test can also move objects or read any behaviour's state. Taking pictures is something only tests do: a game's own scripts have no call for it. What the game saves with Save goes to a file of the test's own, which starts empty: a test never sees or changes your real progress.

Testing Ruby code by itself

Code that needs no running game (a formula, a helper class) can be tested without one. A file of unit tests defines classes that inherit from RedGemTest::Case; every method whose name starts with test_ is a test:

# tests/score_test.rb
class ScoreTest < RedGemTest::Case
  def test_a_coin_is_worth_ten_points
    assert_equal 10, 1 * 10
    assert_in_delta 0.5, Vector3.new(0.5, 0, 0).length
  end
end
./build/src/redgem-test --ruby tests/score_test.rb

It runs in the same Ruby a game runs in, with the scripting API and an empty scene. The assertions are assert, refute, assert_equal, assert_nil, assert_in_delta, assert_vector and assert_raises; a system test has them too.