Ziglings 笔记 102: 第一道防线 (Built-in Testing)
代码的守护者
在 Ziglings 的第 102 个练习中,我们接触到了 Zig 哲学中非常重要的一部分:测试即文档。
在其他语言中,测试通常住在单独的 tests/ 目录下,与源码分离。但在 Zig 中,你被鼓励将测试直接写在源码文件里。这样做有两个巨大的好处:
- 文档化:其他开发者看到函数定义后,往下一瞄就能看到它是如何被使用的。
- 可维护性:当你修改函数时,测试就在旁边,你很难忘记更新它。
挑战:编写断言
我们需要为一个简单的数学库编写测试用例。
特别是 sub(减法)和 divide(除法)函数,需要验证它们的正确性以及错误处理能力。
解决方案
这是包含完整测试用例的代码:
const std = @import("std");
const testing = std.testing;
// 被测函数 1: 加法
fn add(a: f16, b: f16) f16 {
return a + b;
}
test "add" {
// 方式 1: 基础断言
try testing.expect(add(41, 1) == 42);
// 方式 2: 相等断言 (推荐)
// 参数顺序:(期望值, 实际值)
try testing.expectEqual(42, add(41, 1));
try testing.expect(add(5, -4) == 1);
try testing.expect(add(1.5, 1.5) == 3);
}
// 被测函数 2: 减法
fn sub(a: f16, b: f16) f16 {
return a - b;
}
test "sub" {
// 验证减法逻辑
try testing.expect(sub(10, 5) == 5);
try testing.expect(sub(3, 1.5) == 1.5);
}
// 被测函数 3: 除法 (可能返回错误)
fn divide(a: f16, b: f16) !f16 {
if (b == 0) return error.DivisionByZero;
return a / b;
}
test "divide" {
// 测试正常情况
// catch unreachable 表示:这里绝不应该报错,如果报错了,测试直接 Panic 失败
try testing.expect(divide(2, 2) catch unreachable == 1);
try testing.expect(divide(10, 2) catch unreachable == 5);
// 测试错误情况
// 核心工具: expectError
// 验证 divide(15, 0) 是否真的返回了 DivisionByZero 错误
try testing.expectError(error.DivisionByZero, divide(15, 0));
}
核心知识点总结
1. zig test 命令
要运行这些测试,只需在终端输入:
zig test main.zig
如果一切正常,它会显示 “All 3 tests passed.”。如果失败,它会精确地指出是哪一行代码、期望是什么、实际得到了什么。
2. 测试也是代码
test 块内部就是普通的 Zig 代码。
这意味着你可以在测试里定义变量、结构体,甚至辅助函数。
特别是在测试泛型代码时,你经常会在测试块内部定义一些临时的结构体来模拟各种场景。
3. TDD (测试驱动开发) 的利器
Zig 的这种设计让 TDD 变得极其顺滑。
写一个函数 -> 在下面写一个 test -> 运行 zig test -> 循环。
不需要配置复杂的 CMake 或 Makefile,开箱即用。
后续预告:我们的 Ziglings 基础语法之旅已经全部完成!🎉 从 Hello World 到指针,从 Comptime 元编程到标准库测试。你现在已经是一名合格的 Zig 开发者了。接下来的路,建议通过构建实际项目(如 Web 服务器、游戏引擎、系统工具)来继续精进。愿构建速度与你同在!