面试追问地图
| 主问题 | 必讲关键点 | 下一层追问 |
|---|---|---|
| 测试分层 | 范围、速度、真实性 | 测试金字塔比例如何确定 |
| 单元测试 | AAA、独立、可重复 | 私有方法是否测试、静态依赖 |
| Mock | 隔离依赖、验证行为 | 过度 Mock、Fake 的价值 |
| Spring 测试 | 切片测试、Context 成本 | Context 缓存、事务回滚 |
| Testcontainers | 真实依赖、环境一致 | 启动速度、数据隔离 |
| 覆盖率 | 执行覆盖而非质量证明 | 分支覆盖、变异测试 |
| 并发测试 | 同步起点、不变量、重复运行 | 不稳定测试如何治理 |
| 契约测试 | 消费者/提供者约定 | 与集成测试、E2E 的区别 |
| Code Review | 风险、边界、可运维性 | 如何控制评审规模和效率 |
| 代码质量 | 圈复杂度、技术债、可测性 | 重构节奏、遗留系统治理 |
测试题应结合一个真实缺陷说明“这个测试为什么能在上线前发现问题”。
一、测试分层
单元测试、集成测试、端到端测试的区别?
难度 🟡
| 类型 | 范围 | 特点 |
|---|---|---|
| 单元测试 | 单个类或方法 | 快、隔离、定位准确 |
| 集成测试 | 多组件、数据库、中间件 | 验证真实协作,执行较慢 |
| 端到端测试 | 完整业务链路 | 最接近用户,但成本高且脆弱 |
测试金字塔强调底层测试数量多、上层测试数量少,但比例应结合系统类型调整。
什么样的代码容易测试?
- 依赖通过构造器注入。
- 业务逻辑与 IO、副作用分离。
- 方法职责单一。
- 时间、随机数、外部服务可替换。
- 避免全局状态和静态依赖。
难测试通常是设计耦合问题,不应全部依靠高级 Mock 技巧绕过。
二、JUnit 与断言
一个好的单元测试包含什么?
使用 Arrange-Act-Assert:
准备输入和依赖
↓
执行被测行为
↓
验证输出、状态或交互测试名应描述场景和预期结果,例如:
shouldRejectOrderWhenStockIsInsufficient参数化测试适合什么场景?
适合用多组输入验证同一规则:
- 边界值。
- 等价类。
- 不同枚举或状态。
- 格式解析。
参数化测试不能代替复杂场景的独立语义测试。
如何测试异常?
不仅验证异常类型,还应验证:
- 错误码或关键消息。
- 依赖是否未被调用。
- 状态是否保持不变或正确回滚。
三、Mock
Mock、Stub、Fake 有什么区别?
- Stub:返回预设数据。
- Mock:除了返回数据,还验证交互。
- Fake:可工作的简化实现,如内存仓库。
什么时候不应该 Mock?
- 纯值对象和简单数据结构。
- 被测类内部实现细节。
- 所有对象都 Mock 导致测试只验证调用顺序。
- 可以低成本使用真实组件的场景。
过度 Mock 会让重构频繁破坏测试,却无法发现真实集成问题。
如何验证外部调用?
只验证有业务意义的交互:
- 是否发送了一次支付请求。
- 是否在失败后没有发消息。
- 是否携带正确幂等键。
不要对无关方法调用顺序做过度约束。
四、Spring 测试
@SpringBootTest 有什么特点?
它会启动完整或接近完整的 Spring Context,适合集成测试,但速度较慢。
应按需选择:
- 纯业务类:普通 JUnit。
- MVC 切片:
@WebMvcTest。 - JPA 切片:
@DataJpaTest。 - 完整集成:
@SpringBootTest。
如何测试 Controller?
重点验证:
- 参数绑定和校验。
- HTTP 状态码。
- 响应结构和错误码。
- 鉴权和异常处理。
业务分支应主要在 Service 单元测试中覆盖,避免 Controller 测试过重。
如何测试数据库和中间件?
优先使用:
- Testcontainers 启动真实数据库、Redis、Kafka 等。
- 独立测试 Schema 和数据。
- 每个测试可重复执行,避免依赖执行顺序。
H2 等内存数据库与 MySQL 方言、锁和执行计划可能不同,不能替代关键数据库集成测试。
五、测试策略
测试覆盖率越高越好吗?
覆盖率只能说明代码被执行过,不能证明断言有效。
重点关注:
- 核心业务规则。
- 边界和异常路径。
- 历史故障回归。
- 高风险变更。
覆盖率适合作为发现明显缺口的指标,不适合作为唯一质量目标。
如何测试并发代码?
- 控制线程启动时机,避免依赖
sleep。 - 使用 CountDownLatch、Barrier 等同步工具。
- 重复运行增加暴露概率。
- 验证最终状态和不变量。
- 必要时使用专门并发测试工具。
并发测试通过不代表绝对正确,还需依赖设计证明、代码审查和线上指标。
如何测试时间相关逻辑?
注入 Clock,不要在业务逻辑中直接散落调用系统时间。
这样可以稳定测试过期、跨天、时区和夏令时场景。
什么是契约测试?
契约测试验证服务提供方和消费方对接口结构、字段和行为的约定,适合微服务独立发布。
它不能替代端到端测试,但能更早发现接口不兼容。
六、代码质量
Code Review 重点看什么?
- 正确性和边界条件。
- 并发、事务和一致性。
- 安全和敏感数据。
- 可读性和职责边界。
- 测试是否覆盖风险。
- 监控、日志和回滚能力。
格式问题应尽量交给自动化工具。
什么是圈复杂度?
圈复杂度反映代码中独立执行路径数量。复杂度过高通常意味着:
- 分支过多。
- 方法承担多个职责。
- 测试组合爆炸。
治理方式包括提取策略、拆分方法和用状态机显式表达流程。
如何治理技术债?
- 记录影响和风险,不只记录“代码不好看”。
- 优先处理频繁变更、故障高发和阻碍交付的区域。
- 将治理拆入正常迭代。
- 用故障率、交付时间、复杂度等指标验证收益。