最近在公司接手一个老项目,代码改一处,其他地方总出问题。每次上线前都提心吊胆,生怕哪个隐藏 Bug 突然冒出来。后来团队决定引入单元测试,并把它真正“塞”进主框架里,而不是放在角落吃灰。折腾了一阵子,现在回过头看,这个决定确实值了。
为什么不能只写测试,而不集成?
刚开始我们也只是零散地写一些测试用例,跑一下就完事。但问题来了:新同事不知道要跑测试,CI/CD 流程也不检查结果,久而久之,测试代码就成了摆设。就像你买了个闹钟,却从来不设时间——它再准也没用。
只有把单元测试变成开发流程的一部分,比如提交代码前自动跑一遍,失败就拦住,大家才会真正重视它。
怎么融入主框架?以 Spring Boot 为例
我们项目是基于 Spring Boot 的,集成 JUnit 和 Mockito 相对方便。关键是在项目结构和构建脚本里做好配置。
pom.xml 中确保有这些依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
然后在 src/test/java 下写测试类,利用 @SpringBootTest 注解加载上下文:
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void should_return_user_by_id() {
User user = userService.findById(1L);
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1L);
}
}
这样写完,测试就能访问到容器里的 Bean,和真实运行环境更贴近。
让构建过程“卡住”失败的测试
Maven 默认会在 mvn test 或 mvn package 时执行测试,但如果想确保没人绕过去,就得在 CI 阶段加一道关。
比如在 GitHub Actions 中配置:
name: Build and Test
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: '17'
- name: Run tests
run: mvn test
- name: Package if tests pass
run: mvn package
只要有一个测试失败,后续步骤就不会执行,直接提醒开发者去修。
小步快跑,别想着一步到位
一开始我们想给所有旧代码补测试,结果发现太难推进。后来换了个思路:新写的代码必须带测试,老代码改动时顺手补上一点。积少成多,一个月下来覆盖率从不到 20% 涨到了 60% 多。
就像收拾屋子,别指望一天打扫完整个家,每天擦一张桌子也行。
工具只是帮手,习惯才是关键
用了 JUnit、Mockito、Coverage 插件,甚至上了 SonarQube,但最核心的还是团队有没有把写测试当成日常动作。建议在每日站会里提一句“今天测了吗”,慢慢就成了肌肉记忆。
现在我们提交代码前,IDE 自动提示哪些分支没覆盖,顺手补两行测试已经成了条件反射。这种安全感,真不是上线后盯着日志刷新能比的。