# Getting \`zig build test\` to find all the tests in my module

**URL:** <https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276>\
**Category:** Help\
**Tags:** language\
**Created:** [October 6, 2024, 6:51pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276 "2024-10-06T18:51:58Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 6:51pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/1 "2024-10-06T18:51:59Z")

</div>

Edit: Changed title from “Should I put `std.testing.refAllDecls(@This());` in all my files?”

Ziggits docs suggest using

```zig
test {
    std.testing.refAllDeclsRecursive(@This());
}

```

in my `root.zig`

> [@Build System Tricks](https://ziggit.dev/docs?topic=3531#h-4-run-your-test-suite-exposed-with-stdtestingrefalldecls-13):
>
> This community-driven doc is an attempt to expand on the [official Zig Build System documentation](https://ziglang.org/learn/build-system/) as well as the past community contributions by [xq](https://zig.news/xq/series/2), [Sobeston](https://zig.guide/build-system/build-modes/) and others. Here you’ll find a list of tricks on how to use every part of the build system while making sure to conveniently name and lay out your build steps. I. Basic Usage 1) Declare commonly used information upfront // Resolve a query for a custom target const my\_target = b.resolveTargetQuery(.{ .cpu\_arch = .wasm32, .os\_tag =…

But this is problematic when I declare a top-level declaration like so:

```zig
pub const SyncM = packed struct(u64) {
    physical_start_address: u16,
    length: u16,

    /// control register
    control: esc.SyncManagerControlRegister,
    status: esc.SyncManagerActivateRegister,
    enable_sync_manager: EnableSyncMangager,
    syncM_type: SyncMType,
};
pub const SMCatagory = std.BoundedArray(SyncM, max_sm);

```

Because this will reference the `Writer` of a bounded array, which is only valid if the bounded array is full of `u8`.

```zig
zig_install/lib/std/bounded_array.zig:282:13: error: The Writer interface is only defined for BoundedArray(u8, ...) but the given type is BoundedArray(sii.SyncM, ...)
            @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++

```

My first inclination is to put

```zig
test {
    std.testing.refAllDecls(@This());
}

```

at the bottom of all my files, which, after doing it, has found multiple errors in some of my function declarations…which is nice.

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 7:02pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/2 "2024-10-06T19:02:33Z")

</div>

The standard library appears to use a mix of

```zig
test {
    std.testing.refAllDecls(Cpu.Arch);
}

```

and manual references to each sub-module

```zig
test {
    _ = AnyReader;
    _ = AnyWriter;
    _ = @import("io/bit_reader.zig");
    _ = @import("io/bit_writer.zig");
    _ = @import("io/buffered_atomic_file.zig");
    _ = @import("io/buffered_reader.zig");
    _ = @import("io/buffered_writer.zig");
    _ = @import("io/c_writer.zig");
    _ = @import("io/counting_writer.zig");
    _ = @import("io/counting_reader.zig");
    _ = @import("io/fixed_buffer_stream.zig");
    _ = @import("io/seekable_stream.zig");
    _ = @import("io/stream_source.zig");
    _ = @import("io/test.zig");
}

```

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [October 6, 2024, 7:10pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/3 "2024-10-06T19:10:07Z")

</div>

No. I will be very sad if that function survives to 1.0.

My most recent idea to solve this was [introduce `reachable` enum tag to `std.builtin.BranchHint`; all functions reachable by default · Issue #21511 · ziglang/zig · GitHub](https://github.com/ziglang/zig/issues/21511) but it was a seriously flawed idea.

I haven’t given up yet though.

In the meantime, the best thing you can do is try to test your code as much as possible, and for those functions too annoying to test, use `_ = foo;` on them to at least type check them.

Another idea I just thought of is to allow this syntax:

```zig
test foo;

```

which would be equivalent to:

```zig
test {
    _ = &foo;
}

```

This at least looks like a doctest but without the actual work of writing a doctest.

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [October 6, 2024, 7:12pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/4 "2024-10-06T19:12:54Z")

</div>

I am using:

```zig
comptime {
    std.testing.refAllDecls(@This());
}

```

I am using `comptime` instead of `test` because I don’t want to count it as an additional test case. (`refAllDecls` uses `builtin.is_test` to return immediately if the code is not running under a test runner).

I don’t use `refAllDeclsRecursive` because I don’t write deeply nested test cases.

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 7:14pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/5 "2024-10-06T19:14:32Z")

</div>

This is mostly about how to get

```zig
zig build test

```

to find and run all of the tests in all my files.

And less about trying to get the compiler to detect dead code.

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [October 6, 2024, 7:14pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/6 "2024-10-06T19:14:59Z")

</div>

It’s the same problem

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 7:18pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/7 "2024-10-06T19:18:35Z")

</div>

couldn’t resist

 ![image](https://ziggit.dev/uploads/default/original/2X/0/0918a3549096e856d7f69a10e47e1dad456b69a1.jpeg)

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [October 6, 2024, 7:23pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/8 "2024-10-06T19:23:09Z")

</div>

`test foo;`  
where `foo` is a namespace; that means that the following may also be valid:

```zig
test @This();

```

or

```zig
const Self = @This();
test Self;

```

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 7:29pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/9 "2024-10-06T19:29:53Z")

</div>

Maybe something like

```zig
std.testing.refAllTestsRecursive

```

?

But this would likely have the unfortunate side-effect of running tests in the standard library?

Well actually as long I as dont `pub` my `std` it would be fine?

```zig
const std = @import("std");

```

Well it would probably still run the `std.BoundedArray` tests from my example if they exist.

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [October 6, 2024, 8:35pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/10 "2024-10-06T20:35:01Z")

</div>

> [@jeffective](#):
>
> Because this will reference the `Writer` of a bounded array, which is only valid if the bounded array is full of `u8`.

I don’t understand which code actually contains the access to `BoundedArray(sii.SyncM, ...).Writer`

couldn’t you just not access `.Writer` unless it is actually `BoundedArray(u8, ...)`?

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 8:44pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/11 "2024-10-06T20:44:10Z")

</div>

consider the following example:

```zig
const std = @import("std");

// a sub module of this module, that may or may not have test declarations in it.
pub const eni = @import("eni.zig");
/// now imagine about 20 more sub modules...
pub const MyStruct = struct {
    a: u8,
    b: u8,
};

pub const MyStructArray = std.BoundedArray(MyStruct, 32);

test {
    std.testing.refAllDeclsRecursive(@This());
}

```

when run with `zig test` the following compile error is emitted:

```zig
error: The Writer interface is only defined for BoundedArray(u8, ...) but the given type is BoundedArray(test.MyStruct, ...)
            @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++

```

This is because `std.testing.refAllDeclsRecursive(@This());`  
reaches into `MyStructArray` and does this:

```zig
_ = MyStructArray.Writer

```

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [October 6, 2024, 8:53pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/12 "2024-10-06T20:53:20Z")

</div>

So the leading solution (in use by the standard library)

is to do something like this, which isnt too bad

```zig
const std = @import("std");

// a sub module of this module, that may or may not have test declarations in it.
pub const eni = @import("eni.zig");
/// now imagine about 20 more sub modules...
pub const MyStruct = struct {
    a: u8,
    b: u8,
};

pub const MyStructArray = std.BoundedArray(MyStruct, 32);

test {
    _ = @import("eni.zig");
    // 20 more of these...
}

```

or use the non-recursive version and put it at the bottom of all my files

```zig
test {
    std.testing.refAllDecls(@This());
}

```

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [October 6, 2024, 8:57pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/13 "2024-10-06T20:57:03Z")

</div>

Ahh okay, I didn’t think about `refAllDeclsRecursive` itself causing the reference.

Yeah we had a topic about this problem of not being able to avoid this error from comptime code that may not know that accessing a decl may trigger a `@compileError` here:

> [@A not-immediately-fatal way of indicating that a feature is unavailable](https://ziggit.dev/t/a-not-immediately-fatal-way-of-indicating-that-a-feature-is-unavailable/4693):
>
> In many places in the standard library, you see the use of @compileError() as a mean to indicate that a feature is unavailable for one reason or another. Sometimes it’s a function: pub const min = @compileError("deprecated; use @min instead"); Sometimes it’s a type: pub const Writer = if (T != u8) @compileError("The Writer interface is only defined for ArrayList(u8) " ++ "but the given type is ArrayList(" ++ @typeName(T) ++ ")") else std…

Some part of me thought that it would be nice if `@compileError` would just return a compileError-value that encapsulates the error and line information of the error, and then you could use comptime to inspect that error without it actually being thrown as an error. Only when this value gets compiled into the program in some way it would actually be triggered as an error.

Basically it would evaluate to a comptime value which then needs to disappear, but if it is referenced by / compiled into runtime code than it is triggered as error.  
The value also could be opaque if the comptime code should not be able to access to much of the error message.

But I don’t understand the compiler enough, to say if that would work.  
If the comptime type info was just enough to tell that the value resolves to a compile error, then refAllDecls could avoid referencing such declarations.

But from what Andrew wrote above, the `refAllDecls` is supposed to become unnecessary anyway, still being able to avoid `@compileError`s could be useful for other code…

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [October 7, 2024, 11:24pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/14 "2024-10-07T23:24:23Z")

</div>

> [@Sze](#):
>
> Some part of me thought that it would be nice if `@compileError` would just return a compileError-value that encapsulates the error and line information of the error, and then you could use comptime to inspect that error without it actually being thrown as an error. Only when this value gets compiled into the program in some way it would actually be triggered as an error.

If you look at the fields of [StructField](https://ziglang.org/documentation/master/std/#std.builtin.Type.StructField) next to the field (singular) of [Declaration](https://ziglang.org/documentation/master/std/#std.builtin.Type.Declaration) you’ll see a way out of this problem: add a `.type` field to `Declaration`. There are some issues discussing this, as well as having a `compile_error` type as a comptime equivalent of `noreturn`.

There are some difficulties with doing it that way without it forcing eager evaluation, but we need some sort of way to introspect declarations without halting the compilation process. It could be `@TypeOfDecl(thing, "name")`, I don’t mind that at all, as long as it solves the exploding reference problem.

That way [std.testing.refAllDeclsRecursive](https://ziglang.org/documentation/master/std/#std.testing.refAllDeclsRecursive) could look like this:

```zig
pub fn refAllDeclsRecursive(comptime T: type) void {
    if (!builtin.is_test) return;
    inline for (comptime std.meta.declarations(T)) |decl| {
        const DeclT = @TypeOfDecl(T, decl.name);
        if (DeclT == type) {
            switch (@typeInfo(@field(T, decl.name))) {
                .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursive(@field(T, decl.name)),
                else => {},
            }
        }
        if (DeclT != compile_error) {
            _ = &@field(T, decl.name);
        }
    }
}

```

Although the last bit probably isn’t needed because to get the type of a declaration it has be be analyzed.

I don’t actually care whether the function stays or goes, just about having a way to introspect declarations without causing a compile error. It’s a classic use/mention mismatch: we want some kind of way of saying “analyze this, but this function call _will not_ put the type into use in the program, so just tell us if it won’t compile, don’t halt”.

---

<div class="post-metadata">

**Author:** ![objarni](https://ziggit.dev/user_avatar/ziggit.dev/objarni/32/5822_2.png) [@objarni](https://ziggit.dev/u/objarni)\
**Post date:** [July 14, 2025, 11:06am UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/15 "2025-07-14T11:06:14Z")

</div>

Hello, new to Zig!

Today learning about how to discover and run tests, and found this thread. I finally got the tests in my “training.zig” imported to “main.zig” running thanks to you explanation. I did try the other methods mentioned earlier in the thread (refAllDecls and refAllDeclsRecursive) however they didn’t work.

I’m on version 0.14.1.

Curious as to 0.15 ‘way’ of discovering tests? I can see the balance/tension between a slim, fast compiler that only includes what is actually used from ‘top down’ (main/root) and the ‘work in progress, want to run my not-yet-integrated-code’ way of working (my usual stance).

---

<div class="post-metadata">

**Author:** ![jeffective](https://ziggit.dev/user_avatar/ziggit.dev/jeffective/32/3116_2.png) [@jeffective](https://ziggit.dev/u/jeffective)\
**Post date:** [July 14, 2025, 4:04pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/16 "2025-07-14T16:04:43Z")

</div>

the refAllDecls and related methods only work for declarations marked pub. Did you forget to mark the modules you were importing as pub?

If you don’t want them to be pub, the only other way you can make them discoverable during test is to do a test or comptime block and reference that there like I showed.

---

<div class="post-metadata">

**Author:** ![objarni](https://ziggit.dev/user_avatar/ziggit.dev/objarni/32/5822_2.png) [@objarni](https://ziggit.dev/u/objarni)\
**Post date:** [July 14, 2025, 5:04pm UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/17 "2025-07-14T17:04:15Z")

</div>

Good catch, marking the const identifier ‘pub’ made the refAllDecls call run the tests.

In summary:

```zig
// Context: there are two files, main.zig and training.zig.
// training.zig contains some pub fn's with unit tests.
// main.zig contains a trivial main loop which does not use
// any of the pub fn's in training.zig (yet).
// 
// Found 2 ways to get unit tests in training.zig to run
// using ordinary 'zig build test' command:

// Alternative 1
pub const training = @import("training.zig");
test {
    std.testing.refAllDecls(@This());
}

// Alternative 2
test "training" {
    _ = @import("training.zig");
}

// Note: it is also possible to do 'zig build test src/training.zig' to
// run only tests in training.zig, not all tests.

```

---

<div class="post-metadata">

**Author:** ![matklad](https://ziggit.dev/user_avatar/ziggit.dev/matklad/32/2462_2.png) [@matklad](https://ziggit.dev/u/matklad)\
**Post date:** [September 29, 2026, 10:02am UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/18 "2026-09-29T10:02:55Z")

</div>

Hm, this continues to be mightily annoying in the small, when you are just writing something, and are not ready to write a test testing that all the tests are tested.

What’s funny here, though, is that compiler actually _knows_ where all the tests are. As far as I understand it:

- ASTGen is fully eager. All `@import`-reachable files are parsed, even if they don’t end up analyzed further.
- The notion of a test is purely syntactic, the same as with `@improt`. You can’t use reflection to generate tests.

I wonder if it would be a good idea to just error out when unreachable tests are detected? Though, then, again, tests might not _compile_ on some targets. But maybe they should be explicitly marked as skipped then?

---

<div class="post-metadata">

**Author:** ![pzittlau](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/ecae2f/32.png) [@pzittlau](https://ziggit.dev/u/pzittlau)\
**Post date:** [September 29, 2026, 10:24am UTC](https://ziggit.dev/t/getting-zig-build-test-to-find-all-the-tests-in-my-module/6276/19 "2026-09-29T10:24:15Z")

</div>

I’m not a test-driven guy but this is the one case where I actually do the whole “Make it Red; Make it Green” thing. I’ve changed my snippet for tests to basically be this:

```zig
test "test" {
    const io = testing.io;
    const gpa = testing.allocator;
    _ = .{ io, gpa };
    return error.Unimplemented;
}

```

Then I save once and check whether the `watch` mode complains.

> **Real Snippet**
>
> ```zig
> "test": {
> "prefix": ["test", "tes"],
> "body": [
> "test ${1:\"test\"} {",
> " const io = testing.io;",
> " const gpa = testing.allocator;",
> " _ = .{io, gpa};",
> " return error.Unimplemented;$0",
> "}"
> ]
> },
> 
> ```
