Testing for Signals
With GdUnit, you can test signals in your Godot projects. GdUnit provides two tools specifically for working with signals: assert_signal()
and monitor_signals()
.
By using these tools, you can test the emission of signals in your Godot projects and ensure that the correct signals are being sent and received.
Verify Signals
The assert_signal()
an Assertion Tool to verify for emitted signals until a certain time. When the timeout is reached, the assertion fails with a timeout error.
For more details show assert_signal
Here’s an example of using assert_signal()
:
-
extends GdUnitTestSuite class TestEmitter extends Node: signal test_signal_counted(value) signal test_signal() signal test_signal_unused() var _trigger_count :int var _count := 0 func _init(trigger_count := 10): _trigger_count = trigger_count func _process(_delta): if _count >= _trigger_count: test_signal_counted.emit(_count) if _count == 20: test_signal.emit() _count += 1 var signal_emitter :TestEmitter func before_test(): signal_emitter = auto_free(TestEmitter.new()) add_child(signal_emitter) func test_signal_emitted() -> void: # wait until signal 'test_signal' without args is emitted await assert_signal(signal_emitter).is_emitted("test_signal") func test_signal_is_emitted_with_args() -> void: # wait until signal 'test_signal_counted' is emitted with value 20 await assert_signal(signal_emitter).is_emitted("test_signal_counted", [20]) func test_signal_is_not_emitted() -> void: # wait to verify signal 'test_signal_counted()' is not emitted until the first 50ms await assert_signal(signal_emitter).wait_until(50).is_not_emitted("test_signal_counted") func test_is_signal_exists() -> void: var node :Node2D = auto_free(Node2D.new()) assert_signal(node).is_signal_exists("visibility_changed")\ .is_signal_exists("draw")\ .is_signal_exists("visibility_changed")\ .is_signal_exists("tree_entered")\ .is_signal_exists("tree_exiting")\ .is_signal_exists("tree_exited")
-
using System.Threading.Tasks; using GdUnit4.Asserts; using GdUnit4.Core.Signals; using static Assertions; [TestSuite] public partial class SignalAssertTest { private sealed partial class TestEmitter : Godot.Node { [Godot.Signal] public delegate void SignalAEventHandler(); [Godot.Signal] public delegate void SignalBEventHandler(string value); [Godot.Signal] public delegate void SignalCEventHandler(string value, int count); private int frame; public override void _Process(double delta) { switch (frame) { case 5: EmitSignal(SignalName.SignalA); break; case 10: EmitSignal(SignalName.SignalB, "abc"); break; case 15: EmitSignal(SignalName.SignalC, "abc", 100); break; } frame++; } } [TestCase] public async Task IsEmitted() { var node = AutoFree(new TestEmitter())!; await AssertSignal(node).IsEmitted("SignalA").WithTimeout(200); await AssertSignal(node).IsEmitted("SignalB", "abc").WithTimeout(200); await AssertSignal(node).IsEmitted("SignalC", "abc", 100).WithTimeout(200); } [TestCase] public async Task IsNoEmitted() { var node = AddNode(new Godot.Node2D()); await AssertSignal(node).IsNotEmitted("visibility_changed", 10).WithTimeout(100); } [TestCase] public void IsSignalExists() { var node = AutoFree(new Godot.Node2D())!; AssertSignal(node).IsSignalExists("visibility_changed") .IsSignalExists("draw") .IsSignalExists("visibility_changed") .IsSignalExists("tree_entered") .IsSignalExists("tree_exiting") .IsSignalExists("tree_exited"); } }
Monitor Signals
The monitor_signals()
tool allows you to monitor the emission of signals from a specific object. It sets up a signal monitoring system for the specified object, which enables you to capture and analyze the signals emitted during the execution of your test.
-
func monitor_signals(source :Object, _auto_free := true) -> Object:
-
In C#, the monitor is integrated into the AssertSignal and is generated implicitly the first time an assertion is used on an emitter. To visualize this better, you can use StartMonitoring. From this point on, all emitted signals are recorded.
/// <summary> /// Starts the monitoring of emitted signals during the test runtime. /// It should be called first if you want to collect all emitted signals after the emitter has been created. /// </summary> /// <returns></returns> public ISignalAssert StartMonitoring();
Here’s an example of using signal monitors.
-
extends GdUnitTestSuite class MyEmitter extends Node: signal my_signal_a signal my_signal_b(value :String) func do_emit_a() -> void: my_signal_a.emit() func do_emit_b() -> void: my_signal_b.emit("foo") func test_monitor_signals() -> void: # start monitoring on the emitter to collect all emitted signals var emitter_a := monitor_signals(MyEmitter.new()) var emitter_b := monitor_signals(MyEmitter.new()) # verify the signals are not emitted initial await assert_signal(emitter_a).wait_until(50).is_not_emitted('my_signal_a') await assert_signal(emitter_a).wait_until(50).is_not_emitted('my_signal_b') await assert_signal(emitter_b).wait_until(50).is_not_emitted('my_signal_a') await assert_signal(emitter_b).wait_until(50).is_not_emitted('my_signal_b') # emit signal `my_signal_a` on emitter_a emitter_a.do_emit_a() await assert_signal(emitter_a).is_emitted('my_signal_a') # emit signal `my_signal_b` on emitter_a emitter_a.do_emit_b() await assert_signal(emitter_a).is_emitted('my_signal_b', ["foo"]) # verify emitter_b still has nothing emitted await assert_signal(emitter_b).wait_until(50).is_not_emitted('my_signal_a') await assert_signal(emitter_b).wait_until(50).is_not_emitted('my_signal_b') # now verify emitter b emitter_b.do_emit_a() await assert_signal(emitter_b).wait_until(50).is_emitted('my_signal_a')
-
using System.Threading.Tasks; using GdUnit4.Asserts; using GdUnit4.Core.Signals; using static Assertions; [TestSuite] public partial class SignalAssertTest { public sealed partial class MyEmitter : Godot.Node { [Godot.Signal] public delegate void SignalAEventHandler(); [Godot.Signal] public delegate void SignalBEventHandler(string value); public void DoEmitSignalA() => EmitSignal(SignalName.SignalA); public void DoEmitSignalB() => EmitSignal(SignalName.SignalB, "foo"); } [TestCase(Timeout = 1000)] public async Task MonitorOnSignal() { var emitterA = AutoFree(new MyEmitter())!; var emitterB = AutoFree(new MyEmitter())!; // verify initial the emitters are not monitored AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterA, MyEmitter.SignalName.SignalA)).IsFalse(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterA, MyEmitter.SignalName.SignalB)).IsFalse(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterB, MyEmitter.SignalName.SignalA)).IsFalse(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterB, MyEmitter.SignalName.SignalB)).IsFalse(); // start monitoring on the emitter A AssertSignal(emitterA).StartMonitoring(); // verify the emitters are now monitored AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterA, MyEmitter.SignalName.SignalA)).IsTrue(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterA, MyEmitter.SignalName.SignalB)).IsTrue(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterB, MyEmitter.SignalName.SignalA)).IsFalse(); AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitterB, MyEmitter.SignalName.SignalB)).IsFalse(); // verify the signals are not emitted initial await AssertSignal(emitterA).IsNotEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50); await AssertSignal(emitterA).IsNotEmitted(MyEmitter.SignalName.SignalB).WithTimeout(50); await AssertSignal(emitterB).IsNotEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50); await AssertSignal(emitterB).IsNotEmitted(MyEmitter.SignalName.SignalB).WithTimeout(50); // emit signal `signal_a` on emitter_a emitterA.DoEmitSignalA(); await AssertSignal(emitterA).IsEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50); // emit signal `my_signal_b` on emitter_a emitterA.DoEmitSignalB(); await AssertSignal(emitterA).IsEmitted(MyEmitter.SignalName.SignalB, "foo").WithTimeout(50); // verify emitter_b still has nothing emitted await AssertSignal(emitterB).IsNotEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50); await AssertSignal(emitterB).IsNotEmitted(MyEmitter.SignalName.SignalB).WithTimeout(50); // now verify emitter b emitterB.DoEmitSignalA(); await AssertSignal(emitterB).IsEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50); } }