Checking Exit Codes and Signals

Make sure your Zig CLI exits correctly. This guide shows how to check for a specific exit code and how to verify that a process was ended by a signal.

When a command finishes, you usually want to know if it succeeded. You can check this by looking at the exit code or seeing if it was terminated by a signal.

Checking the exit code

A program that finishes normally returns an exit code. By convention, 0 means success and any other number means some kind of error. The RunResult has a code field for this.

Here’s how you can test that your app exits with a specific code.

Zig
test "check non-zero exit code" {
    // Assumes "mycli --fail" is designed to exit with code 42
    const argv = &[_][]const u8{"mycli", "--fail"};
    var result = try cmdtest.run(.{ .argv = argv });
    defer result.deinit();

    try testing.expectEqual(@as(u8, 42), result.code);
}

Checking for signals

Sometimes a process doesn’t exit normally. It might be killed by a signal, like when it crashes or you call std.process.abort().

You can check for this using the term field on RunResult. It tells you how the process terminated.

Zig
test "check for abort signal" {
    // Assumes "mycli --crash" calls abort()
    const argv = &[_][]const u8{"mycli", "--crash"};
    var result = try cmdtest.run(.{ .argv = argv });
    defer result.deinit();

    // The process was terminated by a signal, it didn't exit cleanly.
    try testing.expect(result.term == .Signal);
    try testing.expect(result.term != .Exited);
}

When a process is killed by a signal, it doesn’t have an exit code. In this case, cmdtest sets result.code to 0. So, if you need to know the exact reason it stopped, always check result.term.

Other pages in Writing Tests