5、测试的重要性:自测试代码、单元测试框架、测试驱动开发(TDD)入门
说实话,我见过太多项目死在「不敢改代码」上。
代码越堆越多,谁都不敢动。改一行,怕炸一片。最后整个项目就像个纸糊的巨人,一碰就碎。
为什么会这样?说白了——没有测试。
没有测试的代码,就是定时炸弹
我记得刚入行那会儿,带我的老工程师说过一句话,我到现在都记得:
「代码写出来不是本事,能放心改才是本事。」
怎么才能放心改?答案很简单——有测试兜底。
你想想看,重构的本质是什么?是「改变结构,不改行为」。那你怎么知道行为没变?靠眼睛看?靠脑子记?都不靠谱。只有测试能告诉你:这次改动有没有把原来的逻辑搞崩。
核心观点:没有测试的重构,不叫重构,叫「碰运气式改代码」。
自测试代码:让代码自己证明自己是对的
什么叫自测试代码?说白了就是——写完代码后,跑一下测试,绿了就是对的,红了就是有问题。
不需要人工去验证。不需要手动点来点去。一切自动化。
我在项目中遇到过最典型的反例:一个支付模块,每次改完都要测试人员手动走一遍流程——下单、支付、退款、对账……一套下来半小时。后来我实在受不了,花了一天时间把核心流程写成自动化测试。从那以后,改完代码跑一遍测试,5分钟搞定。
我的建议:自测试代码不是「锦上添花」,而是「保命符」。尤其是做重构,没有自测试,你根本不敢动。
单元测试框架:选对工具,事半功倍
单元测试框架,说白了就是帮你写测试、跑测试的工具。不同语言有不同的选择:
| 语言 | 常用框架 | 特点 |
|---|---|---|
| Java | JUnit 5 | 生态成熟,注解驱动 |
| Python | pytest | 简洁灵活,fixture 强大 |
| JavaScript | Jest | 零配置,快照测试好用 |
| C# | xUnit / NUnit | 与 .NET 深度集成 |
我个人习惯用 JUnit 5 做 Java 项目的单元测试。它的注解体系很清晰,比如 @Test、@BeforeEach、@DisplayName,写起来很顺手。
举个简单的例子:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
@DisplayName("两数相加应该返回正确结果")
void testAdd() {
Calculator calc = new Calculator();
int result = calc.add(2, 3);
assertEquals(5, result);
}
}
你看,就这么几行代码,就能保证 add 方法的行为是确定的。以后不管你怎么重构 Calculator 的内部实现,只要这个测试是绿的,你就放心。
测试驱动开发(TDD):先写测试,再写代码
TDD 是什么?三个步骤循环:
- 红:先写一个失败的测试
- 绿:写最简代码让测试通过
- 重构:优化代码,保持测试绿色
我第一次接触 TDD 时觉得这太反直觉了——代码都没写,先写测试?这不是浪费时间吗?
后来真正用了一次才发现,TDD 最大的好处是:让你从一开始就思考「代码该怎么用」,而不是「代码该怎么写」。
注意:TDD 不是银弹。它适合逻辑密集、业务规则明确的模块。对于 UI 交互、外部依赖多的场景,TDD 的性价比会下降。别盲目套用。
我曾经在一个订单系统的折扣模块上用了 TDD。先写测试:
@Test
@DisplayName("满100减20")
void testDiscountOver100() {
Order order = new Order(120);
double actual = order.calculateDiscount();
assertEquals(20.0, actual);
}
然后写最简实现:
public double calculateDiscount() {
if (totalAmount >= 100) {
return 20;
}
return 0;
}
测试通过后,再考虑要不要抽象成策略模式、要不要支持多种优惠叠加。每一步都有测试保护,改起来心里踏实。
知识体系一览
下面这张图,是我自己梳理的「测试与重构」的关系图。你看一眼就能明白:测试不是独立存在的,它是重构的基石。
避坑指南
我曾经犯过一个低级错误:给一个工具类写了 30 个测试,覆盖率 95%,自我感觉良好。结果重构时发现——测试全绿,但业务逻辑还是错了。
为什么?因为测试只测了「方法返回值」,没测「方法之间的交互」。说白了,测试写得太「单元」了,忽略了集成场景。
我的经验:单元测试要写,但别只写单元测试。关键业务流程一定要有集成测试。测试金字塔的每一层都要覆盖到,别只蹲在塔尖。
小结
测试这件事,说白了就是给代码上保险。你愿意花多少时间写测试,就决定了你以后敢不敢改代码。
我个人习惯是:核心逻辑必须 100% 覆盖,边缘逻辑至少 80%。剩下的 20% 靠代码审查和集成测试补上。
记住一句话:没有测试的代码,不是资产,是负债。