I’ve been learning Zig and am looking for best practices regarding how to structure projects, particularly with respect to where tests should go, and how the compiler infers which files are needed for compilation.
Let’s say I have a super simple project, where I am building a shared library, and some tests that verify its functionality. 8 Unit tests – An Introduction to Zig says the following on structuring tests:
If you look at the source code for most of the files present in the Zig Standard Library1, you can see that the
test
blocks are written together with the normal source code of the library.
…
Each programmer might have a different opinion on this. Some of them might prefer to keep unit tests separate from the actual source code of their application. If that is your case, you can simply create a separatetests
folder in your project, and start writing Zig modules that contains only unit tests
While I can agree with keeping tests alongside the code itself, I personally would prefer to keep the tests separate. This is mainly because if a module file is already a significant number of lines, I don’t want to effectively double (or even more than double) that size by adding extra test code in there as well. I prefer to keep the concerns separate by moving the tests into a tests
folder, but maybe people here could change my mind on that.
However, assuming that a separate folder is the option that I go for, this implies I should structure the project something like this:
build.zig
build.zig.zon
src/
root.zig // Imports everything, including dummy module and tests
dummy.zig // Some implementation I want to test
tests/
all.zig // Imports all test modules, so root.zig only needs @import("tests/all.zig")
dummy.zig // Tests for the implementation above
I have my build.zig
set up like so (relevant parts only here):
const lib = b.addStaticLibrary(.{
.name = "myproject",
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const lib_unit_tests = b.addTest(.{
// Assuming this needs to be the same root file as the library,
// since it's the library we're building tests for?
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
In theory, if root.zig
is importing tests/all.zig
, which is in turn importing tests/dummy.zig
, then running zig build test
should build and run the tests in tests/dummy.zig
, as far as I understand. However, when I do run this command, nothing happens. Even with:
test "Dummy test" {
std.testing.expectEqual(1, 0);
}
Nothing is reported after running zig build test
.
What exactly is going wrong here? Am I misunderstanding how these files are intended to work together?