单元测试入门:从第一个断言开始
写给还没写过测试的你
很多开发者第一次听到“单元测试”,脑子里浮现的是密密麻麻的测试框架文档和复杂的 Mock 配置,于是把这件事一推再推。其实单元测试的起点非常小:给一个函数写一段代码,验证它在给定输入下是不是返回了预期的结果。就这么简单。
什么是单元测试
单元测试是针对代码中最小可测试单元的验证,通常是一个函数或一个类的方法。它有三个特点:粒度小、运行快、相互独立。一个好的单元测试不依赖数据库、不调用外部接口、不读取文件,只关心“这段逻辑本身对不对”。
举个例子,一个计算订单折扣的函数,根据会员等级返回不同的折扣价。它的行为完全由输入决定,这就是最适合写单元测试的地方。
第一个测试:AAA 模式
写测试有一个非常实用的结构,叫 AAA,也就是准备(Arrange)、执行(Act)、断言(Assert)。
@Test
void vipUserGetsTwentyPercentOff() {
// Arrange:准备数据
DiscountCalculator calculator = new DiscountCalculator();
// Act:执行被测方法
double price = calculator.applyDiscount(100.0, "VIP");
// Assert:断言结果
assertEquals(80.0, price, 0.001);
}
三行代码,意思一目了然:准备一个计算器,对 100 元订单按 VIP 折扣计算,断言结果是 80 元。测试失败时,断言会精确告诉你“期望 80,实际是多少”,定位问题只需要看这一处。
测试应该覆盖什么
新手常问:到底要测多少才够?不必追求一步到位,先抓住三类情况:
- 正常路径:最常见的输入,比如普通会员、VIP 会员分别享受什么折扣。
- 边界值:价格为 0、刚好达到满减门槛、超过上限的金额,这些地方最容易藏 bug。
- 异常情况:传入非法的会员等级、负数金额时,函数是抛异常还是返回默认值,行为要符合预期。
覆盖这三类,大部分核心逻辑就有了基本保障。剩下的可以在修 bug 时顺手补上:每修一个 bug,先写一个能复现它的测试,再改代码,bug 就很难卷土重来。
四个容易踩的坑
只测“快乐路径”。 只验证正常输入,边界和异常全靠线上用户发现,测试就失去了大半价值。
测试写得比代码还复杂。 如果一个测试需要十几行准备数据才能跑起来,往往是被测代码的依赖太多、职责不清。先重构代码,让它更容易被测试,而不是给测试打补丁。
过度使用 Mock。 Mock 是为了隔离外部依赖,不是为了把所有协作者都换成假对象。Mock 太多,测试验证的只是“假对象按剧本演出”,代码一改就全线飘红。
把测试当成负担。 测试不是写完功能后的“附加作业”,而是开发过程的一部分。先写测试再写实现听起来激进,但哪怕只是“实现完立刻补测试”,也比拖到上线前集中补要轻松得多。
从哪里开始
如果你所在的项目还没有测试,不用想着一夜之间补齐全部覆盖率。选一个最核心、最稳定的纯函数,比如价格计算、日期换算、字符串处理,给它写三五个测试,跑通后把测试命令接进持续集成。从此每次提交代码,都有一个自动的“守门员”帮你盯着回归问题。
单元测试的价值不是那一行绿色的通过提示,而是它带来的底气:敢重构、敢升级依赖、敢在深夜合并代码。因为你知道,一旦改坏了什么,测试会在第一时间告诉你。
从第一个断言开始,写下你的第一个测试吧。
- 点赞
- 收藏
- 关注作者
评论(0)