8.3. Unit Testing
Vala programs use the same unit-test harness as GLib and GTK: the GLib.Test API. Tests are ordinary Vala functions registered with the harness; when you run the compiled binary, GLib prints TAP-style output and sets the process exit code so CI systems (and meson test) can tell pass from fail.
For a Unit Testing Sample Code see: Testing Samples.
Minimal harness
Every test executable should call Test.init before registering tests, then Test.run after registration:
void add_tests () {
Test.add_func ("/example/math/adds", () => {
assert (1 + 1 == 2);
});
}
void main (string[] args) {
Test.init (ref args);
add_tests ();
Test.run ();
}Compile and run:
valac --pkg glib-2.0 tests.vala
./testsTest.add_func takes a hierarchy path (by convention starting with /) and a callback. Paths group related cases in the TAP log and in tools that understand GLib’s test tree.
Assertions and messages
Plain assert (condition) is the usual choice in Vala tests. When you need a custom failure explanation, record context with Test.message and stop the case with Test.fail, or skip unsupported platforms with Test.skip:
Test.add_func ("/example/strings/concat", () => {
string got = "foo" + "bar";
if (got != "foobar") {
Test.message ("expected foobar, got %s", got);
Test.fail ();
}
});The C GLib headers also expose g_assert_cmpstr-style macros for richer logs; those are not always wrapped in Vala’s glib-2.0 vapi, so many projects stick to assert or explicit Test.message blocks.
Suites and shared setup
For several cases that share expensive setup, register a TestSuite or split setup into helpers called from each Test.add_func callback. GLib also provides Test.add_data_func / Test.add_data_func_full for table-driven cases where the same function runs with different user data (see the GLib documentation for the exact C→Vala naming you need in your Vala version).
Subprocess and timing helpers
GLib can re-run the test binary in a subprocess to isolate crashes or to test MainLoop-driven code. See the subprocess and trap-related symbols on GLib.Test (for example Test.trap_subprocess) when a unit must simulate a separate process or capture fatal errors.
Meson and CI
Meson’s test() target runs an executable and interprets its exit status. A minimal meson.build is shown on the Testing Samples page. In CI, run meson test -C build (or your build directory) so every registered Test.add_func is executed automatically.
See also
- Testing Samples — full copy-paste example and Meson snippet
- GLib.Test reference
- 8.1. Debugging — using debuggers when a test or app crashes
