🎯 今日核心问题
- AI 能生成什么级别的单测?边界和局限在哪?
- 如何让 AI 生成真正有价值的测试,而不只是 happy path?
📖 精读提纲
【一】核心概念
单测 = 行为验证,不是代码覆盖率游戏。AI 最擅长的恰恰是人最容易忽略的:边界 case 和异常 case。你写正常流程,让 AI 帮你想各种"如果……会怎样"。
【二】关键原理
给 AI 提供以下信息,单测质量会大幅提升:
- 被测方法代码(完整,包括方法签名)
- 业务语义:这个方法做什么,有什么业务规则
- 期望的测试类型:正常流程 / 边界值 / 异常情况 / Mock 哪些依赖
- 测试框架版本:JUnit5 / Mockito 版本,避免生成旧 API
【三】实战应用
示例:给 UserService.register() 生成单测
// 被测方法
public Long register(RegisterDTO dto) {
// 1. 校验邮箱唯一
if (userMapper.countByEmail(dto.getEmail()) > 0) {
throw new BusinessException("邮箱已被注册");
}
// 2. 密码加密
String encodedPwd = passwordEncoder.encode(dto.getPassword());
// 3. 保存用户
User user = new User();
user.setEmail(dto.getEmail());
user.setPassword(encodedPwd);
user.setStatus(1);
userMapper.insert(user);
return user.getId();
}
Prompt:
"请为上面的 UserService.register() 方法生成 JUnit5 + Mockito 单测:
- 需要 Mock:userMapper, passwordEncoder
- 覆盖以下 case:
1. 正常注册成功,返回用户ID
2. 邮箱已存在,抛出 BusinessException
3. 密码编码器被正确调用
4. 用户保存时字段(email/status)正确设置
请使用 @ExtendWith(MockitoExtension.class),不要用 Spring 上下文"
AI 生成的单测示例结构:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock private UserMapper userMapper;
@Mock private PasswordEncoder passwordEncoder;
@InjectMocks private UserServiceImpl userService;
@Test
void register_success() {
// given
RegisterDTO dto = new RegisterDTO("test@example.com", "password123");
when(userMapper.countByEmail(dto.getEmail())).thenReturn(0L);
when(passwordEncoder.encode(dto.getPassword())).thenReturn("encoded_pwd");
// when
Long userId = userService.register(dto);
// then
assertNotNull(userId);
verify(userMapper).insert(argThat(user ->
"test@example.com".equals(user.getEmail()) &&
"encoded_pwd".equals(user.getPassword()) &&
user.getStatus() == 1
));
}
@Test
void register_emailExists_throwsException() {
// given
when(userMapper.countByEmail(anyString())).thenReturn(1L);
// when & then
assertThrows(BusinessException.class,
() -> userService.register(new RegisterDTO("exists@example.com", "pwd")));
}
}
【四】常见误区
- ❌ 只让 AI 生成 happy path → 漏掉了最有价值的边界和异常 case
- ❌ 不 Mock 依赖 → AI 可能生成需要真实数据库连接的集成测试
- ❌ 不说测试框架版本 → 可能生成 JUnit4 的
@RunWith而非 JUnit5 的@ExtendWith - ✅ 正确做法:说清楚业务规则,明确要覆盖哪几类 case,指定框架版本
💡 今日金句
单测不是为了让覆盖率好看,而是为了让你改代码时有底气。AI 帮你想边界 case,你负责确认业务正确性。
✏️ 今日练习
选你项目里任意一个 Service 方法(有业务规则的那种,别选只是 CRUD 查询的),让 AI 生成覆盖以下所有类型的单测套件:
- 正常流程(至少1个)
- 边界值(至少2个,如空值、零值、最大值)
- 异常情况(至少2个,如参数非法、依赖抛出异常)
- 验证关键交互(verify Mock 被正确调用)
目标:让 AI 帮你一次生成一个可以直接运行的测试类。