Zig build check and tests

“zig build check” is awesome for quickly getting compile errors. But it seems there is no way to make it check that the tests compile (without building and linking the tests).

Is there a way to make that work? If not, is that a reasonable thing to want?

For dvui we are just getting started with testing, and I’d appreciate any feedback on whether people think that “zig build check” should check that tests compile. Thanks!

I am using:

  • zig build check to have nice and fast diagnostics in my editor
    and
  • zig build test --watch to run the tests in a terminal when anything is changed
1 Like

You need to create a test step and add it to the check step. Importantly, this test step should not be passed to e.g. b.addRunArtifact(); in other words, it needs to be a distinct test step from the one you add to the test step, even if it’s effectively identical.

This is necessary because b.addRunArtifact() will (indirectly) call .getEmittedBin() on the artifact, causing it to get emitted and linked.

1 Like

This is what I hoped, but it doesn’t work that way (at least on zig 0.14). The tests still get compiled and linked (but not run). Did that change on master?

It works for me with 0.14.0.

Can you show the relevant parts of your build script?

Diff on top of zig init:

--- build.zig.orig      2025-04-25 22:33:44.268809320 +0200
+++ build.zig   2025-04-25 22:34:31.924801681 +0200
@@ -93,6 +93,11 @@
     const run_step = b.step("run", "Run the app");
     run_step.dependOn(&run_cmd.step);

+    const check_step = b.step("check", "");
+    check_step.dependOn(&b.addTest(.{
+        .root_module = lib_mod,
+    }).step);
+
     // Creates a step for unit testing. This only builds the test executable
     // but does not run it.
     const lib_unit_tests = b.addTest(.{

Example runs:

❯ zig build test --verbose
/home/alexrp/zig/0.14.0/files/zig test -ODebug -Mroot=/home/alexrp/Source/tests/zig/src/root.zig --cache-dir /home/alexrp/Source/tests/zig/.zig-cache --global-cache-dir /home/alexrp/.cache/zig --name test --zig-lib-dir /home/alexrp/zig/0.14.0/files/lib/ --listen=-
/home/alexrp/zig/0.14.0/files/zig test -ODebug --dep zig_lib -Mroot=/home/alexrp/Source/tests/zig/src/main.zig -ODebug -Mzig_lib=/home/alexrp/Source/tests/zig/src/root.zig --cache-dir /home/alexrp/Source/tests/zig/.zig-cache --global-cache-dir /home/alexrp/.cache/zig --name test --zig-lib-dir /home/alexrp/zig/0.14.0/files/lib/ --listen=-
/home/alexrp/Source/tests/zig/.zig-cache/o/4f573ae8536a78fea0fcb50b9ff45692/test --seed=0xf22012c1 --cache-dir=/home/alexrp/Source/tests/zig/.zig-cache --listen=-
/home/alexrp/Source/tests/zig/.zig-cache/o/8b5770d724fac36c44e19535e53ac029/test --seed=0xf22012c1 --cache-dir=/home/alexrp/Source/tests/zig/.zig-cache --listen=-
❯ zig build check --verbose
/home/alexrp/zig/0.14.0/files/zig test -ODebug -Mroot=/home/alexrp/Source/tests/zig/src/root.zig -fno-emit-bin --cache-dir /home/alexrp/Source/tests/zig/.zig-cache --global-cache-dir /home/alexrp/.cache/zig --name test --zig-lib-dir /home/alexrp/zig/0.14.0/files/lib/ --listen=-

Note the -fno-emit-bin in the second command’s log.

1 Like

Aha! Sorry - what you said the first time was exactly right, but it took me a few passes to understand. Thanks very much for following up!

My build.zig code looked like this:

const tests = self.b.addTest(.{ .root_module = mod, .name = name, .filters = self.test_filters });
if (self.check_step) |step| step.dependOn(&tests.step);
if (self.test_step) |step| step.dependOn(&self.b.addRunArtifact(tests).step);

and my mistake was thinking “the check step is depending on the tests before addRunArtifact() is called”, but like you said simply calling addRunArtifact() on a test that is depended on will cause it to build and link.

So the fix looks like:

if (self.check_step) |step| {
    const tests = self.b.addTest(.{ .root_module = mod, .name = name, .filters = self.test_filters });
    step.dependOn(&tests.step);
}
if (self.test_step) |step| {
    const tests = self.b.addTest(.{ .root_module = mod, .name = name, .filters = self.test_filters });
    step.dependOn(&self.b.addRunArtifact(tests).step);
}

where we duplicate the tests, once for zig build check and once for zig build test

Thank you!

1 Like