可验证设计与测试替身

共 19 题
#

1. 不可变对象(immutable object)的可测试性中天然线程安全、hash 稳定

A 不可变对象仍有可变状态,需要加锁方能线程安全
B 不可变对象天然线程安全、hash 稳定,测试无需考虑状态迁移与并发副作用,确定性高 ✓ 正确答案
C 不可变对象作为 HashMap 的 key 会因为内容变化而失效
D 不可变对象无法被测试,因为没有 setter
#

2. 依赖注入(DI)的可测试性中测试时替换实现

A DI 让类自己 new 依赖,测试无法替换
B DI 只与 Spring 容器相关,与可测试性无关
C DI 通过构造器/接口注入依赖,使被测类只依赖抽象,测试时可替换为 fake/stub/mock 实现隔离 ✓ 正确答案
D 依赖注入后测试必须连接真实数据库
#

3. 函数式编程(FP)的可测试性中组合性、引用透明性

A 纯函数依赖外部状态,测试需要大量 setup
B FP 函数无法组合,测试只能逐个写死
C 引用透明性使输出只由输入决定、测试确定性强,组合性让子函数独立可测、测试可复用 ✓ 正确答案
D FP 增加共享可变状态,降低可测试性
#

4. 可验证设计(verifiable design)中架构层面的可测试性设计(Hexagonal、Clean、Onion)

A 这些架构让领域逻辑依赖数据库和框架,可测试性差
B 可验证设计只与测试工具相关,与架构无关
C 通过依赖倒置与端口抽象让领域逻辑不依赖外部技术,可脱离基础设施独立、快速测试 ✓ 正确答案
D 这些架构增加了测试的复杂度,不利于单元测试
#

5. 纯函数(pure function)的可测试性中无副作用、无全局状态

A 纯函数依赖全局状态,测试需要先设置环境
B 纯函数无法做 I/O,因此无法被测试
C 纯函数无副作用、无全局状态,相同输入必得相同输出,测试确定、简单、可并行 ✓ 正确答案
D 纯函数测试必须 mock 外部依赖
#

6. 面向接口的设计(interface-oriented design)中 mock vs stub 的实现替换

A 接口是替换点,测试可注入 stub 提供数据或 mock 验证交互,选择取决于要验证结果还是交互 ✓ 正确答案
B 面向接口设计让代码依赖具体类,测试无法替换
C mock 和 stub 功能完全相同,没有区别
D 面向接口设计只适用于大型框架,与单元测试无关
#

7. 领域逻辑与技术细节的分离(Hexagonal)中 domain 不依赖 infrastructure

A domain 直接依赖数据库和框架,无法独立测试
B domain 只依赖端口接口、不依赖 infrastructure,可脱离数据库/框架独立测试,适配器单独验证 ✓ 正确答案
C 六边形架构让领域逻辑与基础设施耦合,降低可测试性
D domain 依赖 infrastructure 是正常设计,不影响测试
#

8. Mock 过度使用的反模式中测试与实现细节耦合、重构时大面积失效,如何用真实依赖与契约测试替代?

A 对内部协作对象大量 mock 并断言内部调用,会让测试与实现细节耦合、重构时大面积失效 ✓ 正确答案
B Mock 越多测试越可靠,应尽可能对所有依赖都 mock
C 过度 mock 不会影响测试稳定性,因为 mock 是确定性的
D 契约测试与 mock 完全等价,无区别
#

9. 生产验证的"A/B 测试"(A/B test)中统计显著性的实验

A A/B 测试只需对比两组结果,无需考虑样本量和随机波动
B p 值越小说明样本量越大,与效果无关
C 通过随机分组与统计显著性(p 值、样本量、置信区间)判断差异是否由真实效果引起,避免随机波动误判 ✓ 正确答案
D A/B 测试与金丝雀发布完全相同,都只验证稳定性
#

10. 可测试的 ID 生成(testable ID)中 IDGenerator 接口

A 直接在业务里调用 UUID.randomUUID() 最容易测试,因为值是唯一的
B 测试用真实雪花 ID 才能断言具体值
C ID 生成无法测试,因为每次值都不同
D 通过 IDGenerator 接口抽象并注入可控实现,可确定性断言生成的 ID,也便于换生成策略 ✓ 正确答案
#

11. 可测试的时间(testable time)中 Clock 接口、@ClockProvider

A 直接调用 System.currentTimeMillis() 最容易测试,因为时间是真实的
B 测试必须等待真实时间流逝才能验证过期
C 时间逻辑无法测试,因为时间总是变化的
D 通过注入 Clock 接口,测试用固定/可控时钟,可精确确定地测试超时、过期等时间逻辑 ✓ 正确答案
#

12. 可测试的数据库(testable DB)中 Repository 接口、Testcontainers

A 测试应共享一个全局数据库,以便复用数据
B 所有数据库测试都应使用内存数据库,因为它与生产完全一致
C Testcontainers 只能用于单元测试,无法验证真实 SQL
D 领域逻辑测试用 Repository 接口+内存实现,真实 SQL 验证用 Testcontainers 启动真实数据库容器 ✓ 正确答案
#

13. 可测试的网络(testable network)中 HttpClient 接口

A 通过 HttpClient 接口抽象,用 stub/mock/测试服务器(MockWebServer/WireMock)覆盖成功、错误、超时、重试等场景 ✓ 正确答案
B 直接用真实 HttpClient 测试最可靠,因为网络是真实的
C 网络调用无法测试,因为依赖外部服务
D 测试网络必须连接真实的外部服务
#

14. 可测试的并发与异步(testable async)中如何抽象线程池、调度器与超时策略,以便在测试中控制并发时序?

A 通过抽象线程池/调度器/超时策略,测试注入同步执行器、可控调度器与固定时钟,把时序变成可控变量 ✓ 正确答案
B 并发逻辑无法测试,因为线程调度不可控
C 测试并发应在代码里用固定 sleep 猜测时序
D 异步测试必须用真实线程池并按真实时序运行
#

15. 可测试的外部副作用(testable side effects)中邮件、短信、支付回调等外部副作用如何通过端口抽象隔离并在测试中替换?

A 外部副作用无法测试,因为必须调用真实外部服务
B 副作用触发与否不是业务逻辑,无需测试
C 测试副作用应直接调用真实邮件服务,才能保证真实
D 通过端口抽象隔离副作用,测试注入记录型替身验证"是否触发、参数是否正确",真实发送由适配器负责 ✓ 正确答案
#

16. 可测试的配置与特性开关中配置源抽象与 feature flag 注入如何使测试覆盖多配置组合?

A 直接读取 System.getProperty 和全局静态常量,测试最容易控制
B 通过配置源与 feature flag 抽象注入,测试用内存配置覆盖多配置组合与开关状态 ✓ 正确答案
C 配置只在运行时生效,测试无法影响
D feature flag 只能用于生产,无法在测试中控制
#

17. 行为验证 vs 状态验证的取舍中何时用 spy/mock 验证交互,何时应断言结果状态?

A 所有测试都应使用行为验证,因为能验证实现细节
B 优先用状态验证断言可观察结果,仅当需要验证与外部边界的交互契约时才用 spy/mock 行为验证 ✓ 正确答案
C 状态验证与行为验证完全相同,可互换
D 行为验证永远比状态验证更可靠
#

18. 可测试的随机(testable random)中 Random 接口、seed 可控

A 随机逻辑无法测试,因为每次结果都不同
B 通过 Random 接口抽象与固定 seed,测试可注入可控随机源或复现序列,确定性断言随机决策逻辑 ✓ 正确答案
C 测试随机应直接调用 Math.random(),这样才能保证真实
D seed 可控不影响测试,因为随机无法预测
#

19. 不可测试代码的信号中 new、静态方法、Singleton 与全局状态如何阻碍测试,重构优先级如何排?

A new、静态方法、Singleton、全局状态都能让测试轻松替换依赖
B 全局状态不影响测试,因为测试可以共享
C 无状态纯函数静态工具类(如 StringUtils)最阻碍测试,应最先重构
D new 硬编码依赖、静态方法、Singleton、全局状态阻碍依赖替换与状态隔离;重构优先解决 new 硬编码(构造器注入),再治理静态/单例与全局状态 ✓ 正确答案