持续剖析(Continuous Profiling)

共 62 题
📑 题目列表 62 题
#
★★★

1. @AutoConfigureTestDatabase/@AutoConfigureTestEntityManager

@AutoConfigureTestDatabase 与 @AutoConfigureTestEntityManager 注解在 Spring Boot 测试中各有什么作用?

  • @AutoConfigureTestDatabase 替换数据源
  • @AutoConfigureTestEntityManager 提供 JPA TestEntityManager
  • 切片测试中的应用

@AutoConfigureTestDatabase 用于配置测试时替换应用的数据源,默认用嵌入式数据库(如 H2)替换真实数据源,支持 replace = Replace.ANY(替换所有数据源)或 Replace.NONE(不替换)。它让 JPA 切片测试无需真实数据库即可运行。@AutoConfigureTestEntityManager 提供测试用的 TestEntityManager(Spring Boot 封装的 EntityManager 测试辅助类),用于 @DataJpaTest 等切片测试中注入 TestEntityManager 并执行查询,避免直接管理真实数据库连接。两者常配合 @DataJpaTest 使用,保证测试隔离且快速。

这两个注解让 JPA 测试脱离真实数据库,提升测试速度与隔离性。理解 replace 语义与配合方式,是写好切片测试的基础。

#
★★★

2. @Mock/@InjectMocks/@Captor(Mockito JUnit 5 Extension)

Mockito 的 @Mock、@InjectMocks、@Captor 注解在 JUnit 5 中如何配合使用?

  • @Mock 创建 mock 对象
  • @InjectMocks 注入依赖
  • @Captor 捕获参数

@Mock 创建 mock 对象,模拟依赖行为;@InjectMocks 创建被测对象并自动注入被 @Mock 标注的依赖(按构造器、setter、字段依次注入);@Captor 声明 ArgumentCaptor,用于在 verify 时捕获传入 mock 的参数并断言。在 JUnit 5 中需通过 @ExtendWith(MockitoExtension.class) 启用 Mockito 扩展,使 @Mock 等注解生效。示例:@Mock Foo foo; @InjectMocks Service service; @Captor ArgumentCaptor<String> captor; 然后 verify(foo).bar(captor.capture()); assertEquals("x", captor.getValue());。@InjectMocks 的自动注入省去手动构造依赖。

@Mock/@InjectMocks/@Captor 是 Mockito 单元测试的三件套,配合 MockitoExtension 无样板地 mock 与验证。理解注入顺序与捕获语法是核心。

#
★★★

3. @MockBean(Spring Boot Test)的工作机制

@MockBean 注解在 Spring Boot Test 中的工作机制是什么?

  • @MockBean 定义与用途
  • 替换 ApplicationContext 中的 Bean
  • 与 Mockito 的协作

@MockBean 用于在 Spring Boot 测试中把应用上下文中的某个 Bean 替换为 mock 对象,使测试能隔离被测 Bean 的外部依赖。它把 @MockBean 标注的字段作为 mock 加入 ApplicationContext,并替换同类型的原 Bean。所有注入该类型的 Bean 都会获得 mock。通常配合 @SpringBootTest 使用,用于替换如外部服务客户端、Repository 等依赖。通过 mock 对象可设置 when 行为与 verify 验证。注意 @MockBean 会重建应用上下文,若替换的 Bean 变化或数量多,会降低测试速度(上下文缓存命中率下降)。

@MockBean 依托 Spring 的 BeanDefinition 替换机制,在上下文中注入 mock。它适合集成测试中隔离外部依赖,但需注意上下文重建的成本。

#
★★★

4. @MockBean(已弃用)与 @MockitoBean(Spring Boot 3.4+)在测试替身的演进

@MockBean 与 @MockitoBean(Spring Boot 3.4+)在测试替身方面有何演进?

  • @MockBean 的弃用原因
  • @MockitoBean 的改进
  • 测试替身的演进

@MockBean 在 Spring Boot 3.4+ 被标记为弃用,推荐使用 @MockitoBean(以及 @SpyBean→@MockitoSpyBean)。演进原因:@MockBean 使用 Mockito 的旧 API 且与 Spring 6.1/Mockito 5 的集成方式存在局限,且 @MockBean 会触发上下文重建、影响性能;@MockitoBean 采用更现代的 Mockito 5 集成,支持更灵活的 reset 策略与 Mockito 的新特性,语法与 Mockito 生态更一致。它依然把 Bean 替换为 mock,但生命周期与 reset 行为更可控。迁移时只需替换注解名即可。

@MockitoBean 是 @MockBean 的演进替代,解决旧 API 局限并提升与现代 Mockito 的兼容性。升级 Spring Boot 时应迁移注解。

#
★★★

5. @SpringBootTest 与 ApplicationContext 加载

@SpringBootTest 如何加载 ApplicationContext?其工作机制与注意事项是什么?

  • @SpringBootTest 的作用
  • 完整上下文加载
  • 上下文缓存与耗时

@SpringBootTest 启动完整的 Spring Boot 应用上下文(ApplicationContext),加载所有配置类、Bean,用于集成测试。它默认不启动 Web 服务器(可加 webEnvironment 配置),通过 @Autowired 注入 Bean 进行测试。工作机制:Spring Boot 通过 SpringBootTestContextBootstrapper 引导上下文,加载主配置类(@SpringBootApplication 所在类)。注意事项:完整上下文加载慢、内存占用高,且会执行 Bean 初始化,因此每个测试类若配置相同会复用缓存的上下文(context caching),配置不同则重建。必要时用切片测试(@WebMvcTest 等)替代以提速。

@SpringBootTest 是集成测试的入口,完整上下文验证 Bean 装配与协作。理解上下文缓存机制可减少重复加载,提升测试效率。

#
★★★

6. Mockito 的 mock/spy/when/verify/ArgumentCaptor

Mockito 的 mock、spy、when、verify、ArgumentCaptor 各有什么用途?

  • mock 与 spy 的区别
  • when 的 stub
  • verify 与 ArgumentCaptor

Mockito 中:mock() 创建完全 mock 的对象,所有方法默认返回默认值,需 when 设置行为;spy() 创建真实对象的包装,默认调用真实方法,可对部分方法 stub;when(mock.method()).thenReturn(x) 设置 stub 行为;verify(mock, times(1)).method(args) 验证方法调用次数与参数;ArgumentCaptor 捕获 verify 时传入的参数用于断言。示例:when(paymentService.pay(any())).thenReturn(true); verify(orderService, times(1)).save(any()); ArgumentCaptor<Order> cap = ArgumentCaptor.forClass(Order.class); verify(orderService).save(cap.capture()); assertEquals(100, cap.getValue().getAmount());。这些是 Mockito 单元测试的核心 API。

mock 用于隔离,spy 用于部分真实,when stub 行为,verify 验证交互,ArgumentCaptor 捕获参数。理解组合使用是写出高质量单测的关键。

#
★★★

7. OutputCaptureExtension(JUnit 5 + Logback)的日志断言

OutputCaptureExtension 在 JUnit 5 + Logback 中如何用于日志断言?

  • OutputCaptureExtension 的作用
  • 捕获控制台/日志输出
  • 断言日志内容

OutputCaptureExtension 是 Spring Boot 提供的 JUnit 5 扩展,用于在测试中捕获应用输出到 stdout/stderr 的内容(包括日志),从而断言日志。用法:@ExtendWith(OutputCaptureExtension.class) 并在测试方法参数注入 CapturedOutput output,然后 assertThat(output.getOut()).contains("expected log")output.getAll()。它捕获的是输出,配合 Logback 的 ConsoleAppender 可断言日志内容。工程价值:验证关键日志(如 traceId、错误日志)是否按预期输出,常用于日志相关测试。

OutputCaptureExtension 提供捕获输出并断言的能力,适合验证日志行为。注意它捕获输出而非日志框架内部,需日志输出到控制台。

#
★★★

8. Spring Boot 3.5+ 的 @DataJpaTest(JPA 切片测试)与 @DataRedisTest(Redis 切片测试)的边界

Spring Boot 3.5+ 的 @DataJpaTest 与 @DataRedisTest 切片测试的边界是什么?

  • @DataJpaTest 的扫描范围
  • @DataRedisTest 的扫描范围
  • 切片测试的边界与限制

@DataJpaTest 是 JPA 切片测试,只加载 JPA 相关组件(EntityManager、Repository、JPA 配置),不加载 Web、Service 等无关 Bean,使用嵌入式数据库(默认 H2)。@DataRedisTest 是 Redis 切片测试,只加载 Redis 相关配置(RedisTemplate、RedisConnectionFactory)、@RedisHash 与 Repository,同样不加载无关 Bean。边界的核心是"只加载与切片相关的上下文"以提升测试速度与隔离性。限制:切片测试不加载完整依赖,若被测代码依赖其他 Bean 需手动 @MockBean 或 @Import。边界清晰:JPA 测试只管持久层,Redis 测试只管缓存/存储层。

切片测试通过裁剪上下文加速测试并隔离模块。理解各切片加载的组件范围,是正确使用与避免运行时缺 Bean 的前提。

#
★★★

9. Spring Boot 3.5+ 的 @JsonTest(JSON 序列化测试)与 @RestClientTest(HTTP 客户端测试)的边界

Spring Boot 3.5+ 的 @JsonTest 与 @RestClientTest 的边界是什么?

  • @JsonTest 测试 JSON 序列化
  • @RestClientTest 测试 HTTP 客户端
  • 两者适用范围

@JsonTest 是 JSON 序列化切片测试,加载 Jackson/Gson 等 JSON 序列化组件与配置,用于测试对象的 JSON 序列化/反序列化(@JsonSerialize、@JsonProperty、自定义 ObjectMapper 等),不加载其他业务 Bean。@RestClientTest 是 HTTP 客户端切片测试,加载 RestClient/RestTemplate 相关配置与 MockRestServiceServer,用于测试 REST 客户端的请求构造、响应解析与错误处理,无需真实 HTTP 服务。边界的核心:@JsonTest 专注序列化正确性,@RestClientTest 专注客户端行为。两者都只加载相关切片上下文,提升测试速度与隔离性。

两个切片测试分别聚焦 JSON 模型与 HTTP 客户端。明确边界可避免测试混用,保证测试聚焦与快速。

#
★★★

10. @AutoConfigureWebTestClient(WebFlux 测试)

@AutoConfigureWebTestClient 在 WebFlux 测试中如何应用?

  • WebTestClient 的作用
  • @AutoConfigureWebTestClient 配置
  • WebFlux 端到端测试

@AutoConfigureWebTestClient 用于在 Spring Boot 测试中自动配置 WebTestClient,用于测试 WebFlux/WebMVC 应用或 HTTP 客户端。WebTestClient 是响应式 HTTP 测试客户端,支持 get().uri(...).exchange().expectStatus().isOk().expectBody().json(...) 的链式断言。配合 @SpringBootTest(webEnvironment = RANDOM_PORT/MOCK) 可对完整应用做端到端测试,或配合 @WebFluxTest 做切片测试。它基于 WebFlux 的 WebClient 实现,支持响应式断言。@AutoConfigureWebTestClient 需在类路径有 spring-webflux 相关依赖。

WebTestClient 是响应式测试利器,链式断言清晰。@AutoConfigureWebTestClient 自动装配它,无需手动创建。

#
★★

11. Spring Boot 3.5+ 的 @SpringBootTest(完整上下文)/@WebMvcTest(切片测试)的工程取舍

Spring Boot 3.5+ 的 @SpringBootTest 与 @WebMvcTest 在工程上如何取舍?

  • @SpringBootTest 完整上下文
  • @WebMvcTest 切片测试
  • 测试速度与覆盖的权衡

@SpringBootTest 加载完整上下文,覆盖所有 Bean 协作,适合端到端集成测试,但启动慢、耗内存、依赖外部资源。@WebMvcTest 只加载 Web 层(Controller、@ControllerAdvice、过滤器等),不加载 Service/Repository,可用 @MockBean 隔离依赖,因此启动快、专注 Controller 逻辑,适合 Web 层单元/切片测试。工程取舍:Web 层逻辑用 @WebMvcTest(快、聚焦),跨层协作与关键链路用 @SpringBootTest(完整、可信)。实践中用切片测试覆盖大部分 Web 逻辑,用少量完整集成测试验证关键流程,兼顾速度与覆盖率。

测试金字塔建议快而多的小测试 + 少而慢的大测试。@WebMvcTest 与 @SpringBootTest 分别对应金字塔中层与上层,合理搭配提升效率。

#
★★

12. Spring Boot Test 与 JUnit 5(@ExtendWith(SpringExtension.class))的整合与生命周期

Spring Boot Test 与 JUnit 5(@ExtendWith(SpringExtension.class))如何整合?其生命周期如何?

  • SpringExtension 的作用
  • 测试上下文缓存
  • 生命周期管理

Spring Boot Test 通过 @ExtendWith(SpringExtension.class) 把 Spring 的测试上下文与 JUnit 5 整合。SpringExtension 实现 JUnit 5 的扩展接口,管理 ApplicationContext 的加载、缓存与注入,使 @Autowired、@ActiveProfiles 等生效。Spring 测试的上下文默认缓存(context caching),相同配置的测试类复用同一 ApplicationContext,避免重复加载;测试类默认 PER_METHOD 生命周期(每个测试方法新建实例),可配置 PER_CLASS。通过 @TestInstance(PER_CLASS) 可共享实例。生命周期管理还包括 @BeforeEach/@AfterEach、@SpringBootTest 的上下文等。

SpringExtension 是 Spring 与 JUnit 5 的桥梁,上下文缓存机制是提升测试效率的关键。理解生命周期与缓存可优化测试结构。

#
★★

13. Testcontainers(Postgres/Redis/Kafka)

Testcontainers 如何用于 Postgres/Redis/Kafka 等中间件的集成测试?

  • Testcontainers 的原理
  • 各类容器启动
  • 与测试的集成

Testcontainers 通过 Docker 启动真实中间件容器,供集成测试使用,保证测试与生产环境一致。用法:@Testcontainers + @Container 注解或 GenericContainer/专用容器(如 PostgreSQLContainerRedisContainerKafkaContainer),在测试时启动容器,测试后销毁。配合 @DynamicPropertySource 把容器端口注入 Spring 配置。示例:@Container static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:15"); 工程价值:测试使用真实中间件(而非 mock 或嵌入式),提高可信度,避免 embedded 与生产差异。代价是需要 Docker 环境,启动较慢。

Testcontainers 解决"测试与生产环境不一致"问题,用真实容器跑集成测试。理解生命周期与端口注入是使用关键。

#
★★

14. WireMock 在 Spring Boot 微服务集成测试中的 HTTP 服务模拟

WireMock 如何在 Spring Boot 微服务集成测试中模拟 HTTP 服务?

  • WireMock 的作用
  • stub 的配置
  • 与 Spring 测试的集成

WireMock 用于在测试中模拟 HTTP 服务(外部 API、下游微服务),无需真实依赖。通过 WireMockServer 启动一个本地 HTTP 服务器,用 stubFor(get(urlEqualTo("/api/orders")).willReturn(okJson("...").withHeader(...))) 定义返回;请求可用 verify 验证。与 Spring 集成:通过 @DynamicPropertySource 把 WireMock 的端口注入需要调用的外部服务 base url,或用 @AutoConfigureWireMock 注解自动启动。工程价值:隔离下游服务、控制响应(成功/失败/超时)、验证请求,实现可靠的集成测试。

WireMock 让下游 HTTP 服务可控、可验证,是微服务集成测试的标配。理解 stub 与 verify 是核心。

#
★★

15. wall-clock 剖析与 cpu 剖析在定位 I/O 等待、锁阻塞问题上的差异,为什么 CPU 火焰图看不到等待时间

wall-clock 剖析与 cpu 剖析在定位 I/O 等待、锁阻塞问题上有何差异?为什么 CPU 火焰图看不到等待时间?

  • wall-clock 与 cpu 剖析的区别
  • I/O 等待与锁阻塞的定位
  • CPU 火焰图局限

cpu 剖析(如 async-profiler 的 cpu 事件)只采样正在执行 CPU 指令的线程,反映 CPU 热点;而等待中的线程(I/O 等待、锁阻塞、sleep)不消耗 CPU,因此不会出现在 CPU 火焰图中,导致 CPU 火焰图看不到等待时间。wall-clock 剖析(wall 事件)采样所有线程的完整栈(无论是否在 CPU 上),能反映线程在等待什么(I/O、锁、sleep),从而定位 I/O 等待与锁阻塞。差异:CPU 剖析定位"真正烧 CPU 的代码",wall 剖析定位"时间花在哪(包括等待)"。定位锁阻塞通常用锁剖析(lock 事件)或 JFR 的锁事件。

CPU 火焰图只能看到 CPU 消耗,隐藏等待。用 wall-clock 剖析或锁剖析才能看到 I/O 等待与锁竞争,这是性能分析选型的关键。

#
★★

16. 火焰图如何解读,怎样从形态上区分 CPU 热点、内存分配热点与锁竞争(平顶、宽底、调用栈深度各意味什么)

如何解读火焰图?怎样从形态上区分 CPU 热点、内存分配热点与锁竞争?

  • 火焰图的结构与宽度
  • 平顶/宽底/栈深度的含义
  • 区分热点类型

火焰图以 x 轴(宽度)表示采样占比,y 轴(栈深)表示调用栈深度,宽度越大代表该函数占用的资源比例越高。解读:平顶(顶部宽而平坦)表示热点函数自身耗时高(可能是 CPU 密集型或频繁调用);宽底(底部宽)表示调用方广,热点分散在多个调用路径;调用栈深表示递归或深层调用链。区分热点类型:CPU 热点用 cpu 剖析,火焰图中顶部函数宽代表 CPU 密集;内存分配热点用 allocation 剖析,火焰图展示分配热点(分配速率而非存活内存);锁竞争用 lock 剖析,火焰图展示持锁/等待锁路径。通过选择不同事件类型,火焰图形态对应不同热点。

火焰图是"按事件类型采样"的可视化。解读时要结合事件类型判断热点性质,顶部宽度是关键信号。

#
★★

17. @DataJpaTest/@JdbcTest/@DataMongoTest 的测试切片

@DataJpaTest、@JdbcTest、@DataMongoTest 三种测试切片各自的边界是什么?

  • 三种切片的作用
  • 各自加载的组件
  • 适用场景

@DataJpaTest 是 JPA 持久层切片,加载 JPA 相关组件(EntityManager、JPA Repository),使用嵌入式数据库;@JdbcTest 是 JDBC 切片,加载 JdbcTemplate 与 JDBC 相关配置,用于 JDBC 访问测试,不加载 JPA/ORM;@DataMongoTest 是 MongoDB 切片,加载 MongoDB 相关组件(MongoTemplate、Mongo Repository),可使用嵌入式 MongoDB 或 Testcontainers。三者的边界:分别针对 JPA、JdbcTemplate、MongoDB 三种持久化技术,只加载对应切片上下文,提高测试速度与隔离性。选择取决于项目使用的持久化技术。

三种切片测试对应不同持久化技术,裁剪上下文以聚焦测试。理解各自加载的组件可避免缺 Bean 或误加载。

#
★★

18. @DisplayName/@Disabled/@Tag 的工程价值

@DisplayName、@Disabled、@Tag 注解在 JUnit 5 中的工程价值是什么?

  • @DisplayName 可读性
  • @Disabled 跳过测试
  • @Tag 分组与过滤

@DisplayName 为测试方法/类提供可读的中文/描述性名称,提升报告可读性;@Disabled 禁用指定的测试(如临时失效、依赖外部资源),避免失败干扰;@Tag 给测试打标签(如 "fast"、"slow"、"integration"),用于按标签分组执行、过滤(如 mvn test -Dgroups=fast)或排除(如 CI 中只跑 fast 标签)。三者是 JUnit 5 测试管理的常用工具:DisplayName 提升可读性,Disabled 处理临时跳过,Tag 支持按场景分组执行测试集。

这三个注解改善测试的组织与执行。Tag 与 CI 结合可按需筛选测试,提升测试流水线效率。

#
★★

19. @DynamicPropertySource(Spring Boot 2.2+)与 Testcontainers

@DynamicPropertySource 如何与 Testcontainers 配合动态注入配置?

  • @DynamicPropertySource 的作用
  • 动态注入属性
  • 与 Testcontainers 端口绑定

@DynamicPropertySource 是 Spring Boot 2.2+ 提供的静态方法注解,用于在 ApplicationContext 加载前动态添加属性,常与 Testcontainers 配合:把 Testcontainers 容器的动态端口(如 MongoDB、Postgres 端口)注入 Spring 配置,使应用连接到测试容器。用法:@DynamicPropertySource static void props(DynamicPropertyRegistry registry) { registry.add("spring.data.mongodb.uri", mongodb::getReplicaSetUrl); }。它比 @TestPropertySource 更灵活(从容器实时获取端口),结合容器启动时机,保证属性在上下文创建前就绪。工程价值:让集成测试使用真实容器的动态端口,无需固定端口避免冲突。

@DynamicPropertySource 解决"端口动态未知"问题,把容器端口动态注入配置。理解其静态方法与执行时机是关键。

#
★★

20. @JunitJupiter 的 Spring Boot 4.x 演进

Spring Boot 4.x 中 JUnit Jupiter 的演进是什么?

  • JUnit 5 与 Jupiter 的关系
  • Spring Boot 4.x 的测试支持
  • 测试架构演进

JUnit Jupiter 是 JUnit 5 的编程模型(API + 扩展),Spring Boot 4.x 默认使用 JUnit Jupiter 作为测试基础,提供 @SpringBootTest、切片测试等对 Jupiter 的适配。演进方向:Spring Boot 4.x 基于 Spring Framework 7/JUnit 5.10+,测试支持更完善,如对虚拟线程、响应式测试、Testcontainers 的更好集成,以及更现代的测试工具(如 @MockitoBean 替代 @MockBean)。JUnit Jupiter 的扩展模型(Extension API)支撑 Spring 测试扩展。演进重点是保持 JUnit 5 兼容的同时,增强对现代 Java 与 Spring 特性(虚拟线程、AOT)的测试支持。

Spring Boot 4.x 延续 JUnit 5/Jupiter 生态,并引入更现代的测试替身与对虚拟线程的适配。理解演进可规划升级。

#
★★

21. @Nested 与测试分组

@Nested 如何用于测试分组?其作用是什么?

  • @Nested 内嵌测试类
  • 测试组织结构
  • 与 @Tag 的配合

@Nested 允许在测试类内部定义内嵌测试类,用于按功能/场景分组组织测试,提升可读性与层次结构。内嵌类默认沿用 PER_METHOD 生命周期,每个测试方法会创建新的内嵌类实例(由外部类实例创建)。@Nested 常与 @Tag 配合:外层类可标注公共标签,内嵌类可覆盖或细化。它让测试按"被测行为"分组,如 OrderServiceTest 内嵌套 @Nested class CreateTest@Nested class CancelTest,便于阅读与维护。注意 @Nested 内嵌类不能有 @BeforeAll 等静态方法(PER_CLASS 除外)。

@Nested 提升测试的层次组织与可读性,是 JUnit 5 测试结构化的推荐方式。理解生命周期限制可避免使用错误。

#
★★

22. @ParameterizedTest 与多源(@ValueSource/@MethodSource/@CsvSource)

@ParameterizedTest 与 @ValueSource/@MethodSource/@CsvSource 如何实现参数化测试?

  • @ParameterizedTest 参数化
  • 多种数据源
  • 应用场景

@ParameterizedTest 允许同一测试方法用多组参数执行,减少重复。数据源:@ValueSource(单值数组,如 ints/strings);@MethodSource(方法返回 Stream/List 提供参数);@CsvSource(CSV 格式多列参数);@CsvFileSource(CSV 文件)。示例:@ParameterizedTest @ValueSource(strings = {"a","b"}) void test(String s) {...}@ParameterizedTest @CsvSource({"1,2,3","4,5,9"}) void add(int a,int b,int c){...}。工程价值:用一组数据覆盖多种输入,提升测试覆盖与简洁性。参数过多时配合 @MethodSource 更灵活。

参数化测试用数据驱动方式提升覆盖。选择合适的数据源(ValueSource 简单、MethodSource 复杂、CsvSource 多列)可高效组织测试数据。

#
★★

23. @RestClientTest/@JsonTest/@WebServiceClientTest

@RestClientTest、@JsonTest、@WebServiceClientTest 三个切片测试的边界是什么?

  • 三个切片的作用
  • 各自加载的组件
  • 适用场景

@RestClientTest 用于测试 REST 客户端(RestClient/RestTemplate),加载 HTTP 客户端相关配置与 MockRestServiceServer,验证请求构造与响应解析;@JsonTest 用于测试 JSON 序列化/反序列化,加载 Jackson 等 JSON 组件;@WebServiceClientTest 用于测试 SOAP Web 服务客户端,加载 WebServiceClient 相关配置与 MockWebServiceServer。三者的边界分别对应 REST 客户端、JSON 序列化、SOAP 客户端三个测试领域,各只加载相关切片上下文,提升测试速度与隔离性。选择取决于被测对象类型。

三个切片测试分别聚焦 REST 客户端、JSON、SOAP 客户端。理解各自加载组件可正确选型并避免缺 Bean。

#
★★

24. @TestConfiguration 与嵌套配置

@TestConfiguration 与嵌套配置在测试中如何应用?

  • @TestConfiguration 的作用
  • 嵌套配置类
  • 测试专用 Bean

@TestConfiguration 用于定义测试专用的配置类,提供测试所需的 Bean(如 mock、stub、测试数据源),不会在正常应用启动时加载(只有测试时)。它可作为静态内部类(嵌套配置)标注在测试类内部,通过 @Import 或自动被 @SpringBootTest 识别,添加测试 Bean。嵌套配置使其与测试类内聚,便于管理。示例:@TestConfiguration static class TestConfig { @Bean PaymentGateway gw(){ return mock(PaymentGateway.class); } },在测试类中可用 @Import(TestConfig.class) 或直接作为内部类。工程价值:避免污染生产配置,按需注入测试依赖。

@TestConfiguration 提供按需的测试 Bean 注入,内聚且不污染生产。理解它与普通配置的加载差异(仅测试)是关键。

#
★★

25. @WebMvcTest 与契约测试的取舍

@WebMvcTest 与契约测试(Contract Testing)的取舍是什么?

  • @WebMvcTest 的定位
  • 契约测试的定位
  • 两者的互补与取舍

@WebMvcTest 是 Web 层切片测试,验证 Controller 的请求处理、参数绑定、响应与 @ControllerAdvice,用 @MockBean 隔离 Service,快速且聚焦于"本服务 Web 层"逻辑。契约测试(如 Pact、Spring Cloud Contract)验证"服务间接口契约"的一致性,即消费者端的请求与生产者端的响应是否符合约定,防止服务间接口变更导致不兼容。取舍:@WebMvcTest 用于验证"本服务内部 Web 层行为正确",契约测试用于验证"跨服务的接口契约一致"。两者互补:@WebMvcTest 保证单侧实现正确,契约测试保证双侧接口匹配。若追求快速单侧验证用 @WebMvcTest,若关注服务间集成可用契约测试。

@WebMvcTest 与契约测试解决不同层次的问题:内部行为 vs 跨服务契约。合理组合能兼顾单侧质量与接口兼容性。

#
★★

26. @WebMvcTest(@AutoConfigureMockMvc)的切片测试

@WebMvcTest 与 @AutoConfigureMockMvc 如何实现 Web 层切片测试?

  • @WebMvcTest 的切片范围
  • MockMvc 的注入
  • @AutoConfigureMockMvc 的作用

@WebMvcTest 是 Web 层切片测试,只加载 Controller、@ControllerAdvice、转换器、Web 相关配置,不加载 Service/Repository,配合 @MockBean 隔离依赖。MockMvc 是 Web 层测试的入口,@AutoConfigureMockMvc 自动配置 MockMvc Bean 供注入(@WebMvcTest 默认已包含,也可显式标注)。用法:@WebMvcTest(UserController.class) @AutoConfigureMockMvc class UserTest { @Autowired MockMvc mvc; @MockBean UserService svc; @Test void test() throws Exception { mvc.perform(get("/users/1")).andExpect(status().isOk()).andExpect(jsonPath("$.name").value("x")); } }。工程价值:快速验证 Controller 逻辑,无需启动完整上下文。

@WebMvcTest + @AutoConfigureMockMvc 是 Web 层切片测试的标准组合,通过 MockMvc perform/andExpect 做断言,配合 @MockBean 隔离依赖。

#
★★

27. Awaitility 如何做异步断言,相比 Thread.sleep 轮询它在可读性与超时控制上有何优势,常见 untilAsserted 用法有哪些

Awaitility 如何做异步断言?相比 Thread.sleep 轮询它在可读性与超时控制上有何优势?常见 untilAsserted 用法有哪些?

  • Awaitility 的异步等待
  • 相比 Thread.sleep 的优势
  • untilAsserted 用法

Awaitility 提供流畅的异步断言 API,轮询等待条件满足,避免 Thread.sleep 固定等待的脆弱性。优势:可读性强(await().atMost(5s).until(...) 表达清晰)、超时控制精确(atMost/withTimeout)、轮询间隔可配(pollInterval)、失败时输出清晰错误。常见用法:await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> assertThat(queue.size()).isEqualTo(3));until(() -> service.isReady())。它自动重试直到条件满足或超时,相比 Thread.sleep 更可靠、不浪费固定等待时间。工程价值:异步任务、消息消费、事件处理的测试必备。

Awaitility 以声明式轮询替代脆弱的 sleep,提升异步测试的稳定性与可读性。理解 atMost/untilAsserted 是核心用法。

#
★★

28. BDDMockito.given/thenShould 的 BDD 风格

BDDMockito 的 given/thenShould 如何实现 BDD 风格测试?

  • BDD 风格 given/when/then
  • given/thenShould 语法
  • 可读性提升

BDDMockito 提供 BDD 风格的 Mockito API,用 given(...).willReturn(...) 替代 when(...).thenReturn(...),用 then(...).should(...) 替代 verify(...),使测试符合 given/when/then 结构,提升可读性。示例:given(orderRepo.findById(1L)).willReturn(Optional.of(order)); orderService.get(1L); then(orderRepo).should().findById(1L);。BDD 风格让测试读起来像行为描述,便于业务理解与维护。BDDMockito 是 Mockito 的静态方法门面,与普通 Mockito 混用需注意静态导入。

BDD 风格把测试组织成 given/when/then,提升可读性与业务对齐。given/thenShould 是 BDDMockito 的核心语法。

#
★★

29. Cypress/Playwright(前端)与 Spring 测试的边界

Cypress/Playwright(前端)与 Spring 测试的边界是什么?

  • 前端 e2e 测试
  • Spring 后端测试
  • 两者分工

Cypress/Playwright 是前端端到端(e2e)测试工具,在真实浏览器中模拟用户操作,验证前端页面行为与交互,可对接真实或 mock 后端。Spring 测试(JUnit、MockMvc、Testcontainers)验证后端 Java 逻辑、接口、数据层。边界:前端 e2e 测试关注"用户视角的 UI 行为",Spring 测试关注"后端业务逻辑与接口正确性"。它们通常在 CI 中分层执行:后端单元/集成测试先行,前端 e2e 测试在后,前端 e2e 可依赖已部署的测试环境或 mock API。分工清楚:前端测试不重复后端逻辑验证,后端测试不验证 UI。两者互补覆盖全栈。

前端 e2e 与后端测试分层,避免重复验证。前端关注 UI 交互,后端关注逻辑与接口,配合 CI 分层执行。

#
★★

30. JUnit 5 与 JUnit 4 的差异(架构、扩展)

JUnit 5 与 JUnit 4 在架构与扩展上有何差异?

  • 平台/引擎/编程模型分层
  • 扩展模型
  • 兼容性

JUnit 5 采用模块化架构,分为 JUnit Platform(测试发现与执行的基础)、JUnit Jupiter(JUnit 5 的编程模型与扩展)、JUnit Vintage(运行 JUnit 3/4 的引擎)。相比 JUnit 4 的单一 jar 与注解模型,JUnit 5 引入了强大的扩展模型(Extension API),取代 JUnit 4 的 @RunWith/自定义 Runner,通过 Extension 接口(如 BeforeEachCallback、AfterEachCallback、ParameterResolver)实现更灵活的扩展,如参数注入、条件执行、生命周期管理。JUnit 5 还支持 @Nested、@ParameterizedTest、@DisplayName、@Tag 等新特性。兼容性:JUnit 5 通过 Vintage 引擎运行旧测试。

JUnit 5 的分层架构与扩展模型是其核心优势,extension 提供了更强大的自定义能力。Vintage 引擎保证旧测试兼容。

#
★★

31. JUnit 5 的 @Suite 与聚合测试套件

JUnit 5 的 @Suite 注解如何实现聚合测试套件?

  • @Suite 注解
  • 聚合多个测试类
  • 选择与过滤

@Suite 用于聚合多个测试类为一个测试套件,通过 @SelectClasses、@SelectPackages、@SelectClasspathResource 等选择要运行的测试。示例:@Suite @SelectClasses({UserTest.class, OrderTest.class}) class AllTests {}@SelectPackages("com.example.service")。@Suite 支持 @IncludeTags/@ExcludeTags 按标签过滤,配合 @SuiteDisplayName 设置名称。它是 JUnit 5 的聚合测试机制,替代 JUnit 4 的 @RunWith(Suite.class),用于组织与批量运行测试。需要 junit-platform-suite 依赖。

@Suite 提供结构化聚合测试的能力,按类/包/标签选择运行。配合 Tags 可灵活组合测试集。

#
★★

32. JUnit 5 的 Assumptions 与条件执行

JUnit 5 的 Assumptions 与条件执行如何工作?

  • Assumptions 的概念
  • assumeTrue/assumeThat
  • 跳过测试

Assumptions(假设,JUnit 5 的 Assumptions.assumeTrue 等)用于在条件不满足时跳过测试(而非失败),常用于环境依赖(如只有特定数据库/操作系统才运行)。示例:assumeTrue("CI".equals(System.getenv("ENV"))); 不满足则测试被跳过(SKIPPED)。JUnit 5 还提供条件注解如 @EnabledOnOs、@EnabledIf、@DisabledIfEnvironmentVariable 等实现条件执行。Assumptions 与断言(Assertions)的区别:假设不满足跳过,断言不满足失败。工程价值:按环境/条件控制测试执行,避免不适用场景下报错。

Assumptions 让测试在条件不满足时优雅跳过,是环境相关测试的常用手段。理解假设与断言的差异是核心。

#
★★

33. JUnit 5 的 ExtensionModel(BeforeEachCallback/AfterEachCallback)

JUnit 5 的扩展模型(BeforeEachCallback/AfterEachCallback)如何工作?

  • Extension API
  • BeforeEachCallback/AfterEachCallback
  • 自定义扩展

JUnit 5 的扩展模型通过实现 Extension 接口扩展测试行为。BeforeEachCallback 在测试方法执行前回调,AfterEachCallback 在测试方法执行后回调,用于在测试前后做统一处理(如初始化/清理资源、开启事务、设置上下文)。实现自定义扩展:class MyExtension implements BeforeEachCallback, AfterEachCallback { ... },通过 @ExtendWith 注册。此外还有 BeforeAllCallback、ParameterResolver、TestExecutionExceptionHandler 等扩展接口。扩展模型比 JUnit 4 的 Runner 更灵活,是 JUnit 5 的核心特性。

Extension 模型提供面向切面的测试扩展能力,BeforeEachCallback/AfterEachCallback 是生命周期扩展的典型。理解可自定义测试行为。

#
★★

34. JUnit 5 的 TestReporter/TestInfo 注入

JUnit 5 的 TestReporter 与 TestInfo 注入如何工作?

  • TestInfo 提供测试元信息
  • TestReporter 发布报告
  • 参数注入机制

JUnit 5 支持在测试方法参数中注入 TestInfo 与 TestReporter。TestInfo 提供测试名称、显示名、标签、类/方法等元信息;TestReporter 用于发布额外的报告信息(键值对),可在测试报告中查看。示例:void test(TestInfo info, TestReporter reporter) { reporter.publishEntry("key", info.getDisplayName()); }。这是 JUnit 5 参数注入机制(ParameterResolver)的应用,由 Jupiter 内置解析器注入。工程价值:在测试中获取上下文信息或输出诊断到报告。

TestInfo/TestReporter 通过参数注入提供测试上下文与报告能力,是 JUnit 5 内置扩展的体现。理解参数注入机制。

#
★★

35. JsonPath/JsonPathResultMatchers 的断言

JsonPath 与 JsonPathResultMatchers 如何用于 JSON 断言?

  • JsonPath 表达式
  • 断言 JSON 字段
  • MockMvc 集成

JsonPath 是 JSON 查询表达式语言,用于从 JSON 中提取字段。JsonPathResultMatchers 是 MockMvc 的 ResultMatcher,用于对响应 JSON 做断言。用法:mvc.perform(get("/users/1")).andExpect(jsonPath("$.name").value("Alice")).andExpect(jsonPath("$.orders[0].total").value(100)).andExpect(jsonPath("$.age").isNumber()).andExpect(jsonPath("$.tags[?(@.x=='y')]").exists());。它支持字段取值、数组索引、过滤表达式、类型断言等。工程价值:对 REST 接口响应做精细的 JSON 断言,验证字段正确性。

JsonPath 断言是 REST 测试的核心能力,支持字段、数组、过滤表达式。结合 MockMvc 的 andExpect 做声明式断言。

#
★★

36. MockMvc/WebTestClient 的 perform/exchange

MockMvc 的 perform 与 WebTestClient 的 exchange 在测试中如何使用?

  • MockMvc perform 发起请求
  • WebTestClient exchange
  • 断言语义

MockMvc 的 perform() 发起 HTTP 请求到 MockMvc 模拟的 MVC 层,返回 ResultActions,可链式 .andExpect(...) 断言(状态码、响应体、header、JSONPath)。WebTestClient 的 exchange() 发送请求并返回响应,用 .expectStatus().expectBody() 链式断言,是响应式测试客户端。两者都用于 HTTP 层测试,但 MockMvc 用于 Servlet MVC(@WebMvcTest/@SpringBootTest),WebTestClient 用于 WebFlux 或统一测试。示例:MockMvc mvc.perform(get("/x")).andExpect(status().isOk());WebTestClient client.get().uri("/x").exchange().expectStatus().isOk()

perform 与 exchange 分别是 MockMvc 与 WebTestClient 的请求入口。选择取决于被测技术栈(Servlet vs WebFlux)。

#
★★

37. Mockito 的 Answer/doAnswer 自定义返回

Mockito 的 Answer 与 doAnswer 如何自定义 mock 行为?

  • Answer 接口
  • doAnswer 自定义返回
  • 场景应用

Answer 是 Mockito 的接口,用于自定义方法调用时的行为,可基于调用参数、调用次数等动态返回。doAnswer 用于设置自定义 Answer:doAnswer(invocation -> { Object arg = invocation.getArgument(0); return arg.equals("x") ? 1 : 2; }).when(mock).method(any());。它比 when(...).thenReturn 更灵活,适合需要动态计算返回值、抛异常、访问调用上下文的场景。也可用 thenAnswer。工程价值:复杂逻辑的 mock 行为(如基于参数返回不同结果、模拟重试)。

Answer/doAnswer 提供动态 mock 行为,适合复杂返回逻辑。理解 InvocationOnMock 提供的参数与方法信息是核心。

#
★★

38. Mockito 的 RETURNS_DEEP_STUBS 与链式 stub

Mockito 的 RETURNS_DEEP_STUBS 与链式 stub 如何工作?

  • RETURNS_DEEP_STUBS 深度 mock
  • 链式调用 mock
  • 适用场景

RETURNS_DEEP_STUBS 是 Mockito 的 Answer 选项,用于 mock 深度嵌套的链式调用,自动为链上的每个对象创建 mock,无需逐层 mock。用法:Foo foo = mock(Foo.class, RETURNS_DEEP_STUBS); when(foo.getBar().getBaz().getName()).thenReturn("x");。它简化深链式调用(如 getter 链)的构造,但过度使用会掩盖结构问题、降低测试可读性,且当链中某层返回 null 时行为需注意。工程价值:对不可避免的深链式 API 快速 mock,但应保持谨慎。

RETURNS_DEEP_STUBS 自动 mock 深链,减少样板代码,但可能掩盖设计缺陷。适合第三方深链式 API 的快速测试。

#
★★

39. Mockito.inOrder 的有序验证

Mockito 的 inOrder 如何验证方法调用的顺序?

  • inOrder 验证顺序
  • 与 verify 的配合
  • 适用场景

Mockito 的 inOrder 用于验证 mock 方法按特定顺序被调用。用法:InOrder inOrder = inOrder(orderService, paymentService); inOrder.verify(paymentService).pay(any()); inOrder.verify(orderService).save(any()); 验证 pay 先于 save 被调用。inOrder 可以跨多个 mock 验证顺序。它与普通 verify 的区别:普通 verify 只验证调用次数,inOrder 额外验证相对顺序。适用场景:验证有状态流程或依赖顺序的业务逻辑(如先扣款后下单)。

inOrder 验证调用顺序,适用于顺序敏感的业务流程。理解它只能验证 inOrder 中声明的调用,未声明的调用不参与顺序校验。

#
★★

40. Mockito.mockStatic(Mockito 3.4+)的静态方法 mock

Mockito 的 mockStatic 如何 mock 静态方法?

  • mockStatic 用法
  • 静态方法 mock
  • 生命周期与限制

Mockito 3.4+ 的 mockStatic 支持 mock 静态方法。用法:try (MockedStatic<Utility> mock = mockStatic(Utility.class)) { mock.when(() -> Utility.getId()).thenReturn(1L); ... },MockedStatic 需 try-with-resources 关闭以恢复原静态方法。它基于 mockito-inline(inline mock maker)实现。限制:静态 mock 作用域内生效,需在 try 块内使用;会增加测试复杂度与性能开销;过度使用静态 mock 往往暗示代码设计可改进(静态依赖)。工程价值:测试依赖静态方法的旧代码。

mockStatic 使静态方法可测,但需注意作用域与关闭。它也是"静态依赖难测"的缓解手段,应鼓励 IoC 设计。

#
★★

41. Mockito.spy 的部分 mock

Mockito 的 spy 如何实现部分 mock(部分真实调用)?

  • spy 的概念
  • 部分方法 stub
  • 与 mock 的区别

Mockito 的 spy() 创建真实对象的包装,默认调用真实方法,但可对部分方法进行 stub(部分 mock)。用法:List<String> list = spy(new ArrayList<>()); when(list.size()).thenReturn(100);,此时 size() 返回 100,其余方法走真实实现。与 mock 的区别:mock 默认所有方法返回默认值,spy 默认走真实方法。spy 也支持 verify 验证真实方法的调用。注意 spy 对 final 方法/私有方法有限制,且 when 对 spy 的 stub 可能触发真实方法(需用 doReturn 避免副作用)。工程价值:对真实对象做少量方法覆盖,测试真实逻辑 + 局部替换。

spy 用于"真实对象 + 部分桩",是部分 mock 的实现。理解 mock 与 spy 的默认行为差异,以及 doReturn 避免副作用是核心。

#
★★

42. Mockito.verify 的 times/never/atLeast/atMost

Mockito 的 verify 如何用 times/never/atLeast/atMost 控制验证次数?

  • verify 次数验证
  • times/never/atLeast/atMost
  • 默认 times(1)

Mockito 的 verify(mock, times(n)).method() 验证方法被调用 n 次;verify(mock, never()).method() 验证从未调用;verify(mock, atLeast(n)).method() 验证至少调用 n 次;verify(mock, atMost(n)).method() 验证至多调用 n 次。省略次数参数时默认 times(1)。示例:verify(orderService, times(1)).save(any()); verify(paymentService, never()).pay(any()); verify(repo, atLeast(2)).findAll();。工程价值:精确控制对交互的验证,捕捉多余或缺失的调用。

次数验证是交互测试的核心。times/never/atLeast/atMost 覆盖不同验证需求,默认 times(1) 是最常用。

#
★★

43. Pyroscope/Parca 等持续剖析平台如何以低开销长期采集并保留 profile 数据,数据保留与降采样策略如何设计

Pyroscope/Parca 等持续剖析平台如何以低开销长期采集并保留 profile 数据?数据保留与降采样策略如何设计?

  • 持续剖析平台架构
  • 低开销采集原理
  • 数据保留与降采样

持续剖析平台(Pyroscope、Parca)通过 agent 在被测应用中持续采样(采样式剖析,如 async-profiler),以低开销(通常 <1-5% CPU)采集 profile 数据并定期上报,长期运行。为控制开销与存储,采用降采样与数据保留策略:降低采样频率(如 CPU 采样 10-100Hz)、按时间粒度降采样(近期高精度、历史低精度)、聚合(相同调用栈合并计数)、设置保留期(如 30 天原始 + 降采样长期)。数据按标签(服务、实例、函数)索引,支持按时间范围查询与火焰图对比。Pyroscope 采用"fetch 模型"优化,Parca 用 eBPF 采集。设计目标是平衡"可诊断性"与"存储/开销成本"。

持续剖析要长期稳定运行,核心是低开销采样 + 分层降采样 + 保留策略。理解采样频率与保留期权衡是设计关键。

#
★★

44. Resilience4j 的测试支持

Resilience4j 如何提供测试支持?

  • Resilience4j 的测试工具
  • 熔断/限流/重试测试
  • 断言与模拟

Resilience4j 提供测试支持,用于验证熔断器、限流器、重试、Bulkhead 等弹性组件的行为。通过 Resilience4j 的测试工具(如 CircuitBreakerRegistryTimeLimiterRegistry)可手动创建配置并注入测试;可模拟故障注入(如 cb.acquirePermission() 判断熔断、抛出 CallNotPermittedException)、验证状态转换(CLOSED→OPEN→HALF_OPEN)、用 Retry 测试重试次数等。Spring Boot 集成下可用 @CircuitBreaker 注解,测试时用 Resilience4jAutoConfiguration 或手动配置 registry。工程价值:无需真实故障即可验证弹性逻辑。

Resilience4j 测试通过可控状态与故障注入验证弹性行为。理解 registry 与状态机是测试的关键。

#
★★

45. Spring REST Docs 的工程价值

Spring REST Docs 的工程价值是什么?

  • REST Docs 生成文档
  • 测试驱动文档
  • 与测试的集成

Spring REST Docs 通过测试自动生成 REST API 文档,保证文档与代码一致。它基于 MockMvc/WebTestClient 测试,在测试中记录请求/响应,生成 AsciiDoc 片段,最终由 Asciidoctor 生成 HTML 文档。工程价值:文档由测试驱动,与实现同步更新,避免手工文档过时;支持请求/响应示例、字段说明、片段自定义;与测试集成,文档准确性有保障。相比 Swagger(运行时从代码生成),REST Docs 是"测试驱动、精确到示例",更强调文档与实测一致。

REST Docs 的价值在于"文档即测试产物",确保文档与实现一致。理解其测试驱动生成流程是核心。

#
★★

46. Test Interceptor 与 Dynamic Tests

JUnit 5 的 Test Interceptor 与 Dynamic Tests 如何工作?

  • TestIntercept/扩展拦截
  • DynamicTest 动态测试
  • 适用场景

JUnit 5 中,Test Interceptor 通常指扩展模型中的拦截接口(如 TestExecutionExceptionHandlerBeforeTestExecutionCallbackInvocationInterceptor),用于在测试执行前后拦截,实现超时控制、重试、异常处理等。Dynamic Tests 是动态生成的测试(@TestFactory 返回 DynamicTest 流),在运行时生成多个测试用例,适合数据驱动、参数动态确定的场景。示例:@TestFactory Stream<DynamicTest> factory() { return data.stream().map(d -> DynamicTest.dynamicTest("test", () -> { ... })); }。Dynamic Tests 与 @ParameterizedTest 的区别:DynamicTest 更灵活,可动态决定测试数量与名称。

拦截器提供面向切面的测试扩展,DynamicTest 提供运行时动态生成测试的能力。两者增强测试的灵活性。

#
★★

47. TestInstance 与 PER_METHOD/PER_CLASS 的取舍

TestInstance 的 PER_METHOD 与 PER_CLASS 生命周期如何取舍?

  • PER_METHOD 默认生命周期
  • PER_CLASS 共享实例
  • 取舍

JUnit 5 默认 PER_METHOD:每个测试方法创建新的测试类实例,隔离性好、无共享状态,但无法用 @BeforeAll 非静态方法(需静态);PER_CLASS:整个测试类共享一个实例,@BeforeAll/@AfterAll 可以非静态,实例字段可在测试间共享,适合需要共享状态(如共享连接)或减少实例创建的场景。取舍:PER_METHOD 更隔离、更安全,是默认推荐;PER_CLASS 共享状态、减少创建开销,但需注意测试间状态污染。可用 @TestInstance(TestInstance.Lifecycle.PER_CLASS) 显式设置。工程上默认用 PER_METHOD,特殊场景(如共享资源)用 PER_CLASS。

生命周期选择影响隔离性与性能。PER_METHOD 隔离安全,PER_CLASS 共享高效但需防污染。理解差异是选择关键。

#
★★

48. localstack/embedded-postgres 等嵌入式中间件

localstack 与 embedded-postgres 等嵌入式中间件在测试中如何应用?

  • localstack 模拟 AWS 服务
  • embedded-postgres 嵌入式数据库
  • 与 Testcontainers 的比较

localstack 用于本地模拟 AWS 服务(S3、SQS、DynamoDB 等),便于本地与 CI 测试 AWS 依赖,无需真实 AWS 账号。embedded-postgres 是嵌入式 PostgreSQL,在 JVM 进程内启动真实 Postgres 实例,用于数据库集成测试。它们与 Testcontainers 的比较:Testcontainers 用 Docker 启动真实容器(更接近生产),localstack/embedded-postgres 是轻量替代(无需 Docker 或针对特定场景)。选择:有 Docker 且需要真实服务用 Testcontainers;本地快速或受限环境用 embedded-postgres/localstack。嵌入式中间件启动更快,但可能与生产环境有差异。

嵌入式中间件提供轻量测试替代,但需权衡与生产的差异。localstack 适合 AWS 依赖,embedded-postgres 适合数据库测试。

#
★★

49. 如何把持续剖析与 Metrics、Tracing 关联,从异常火焰图下钻到具体请求与代码行的完整排查路径是什么

如何把持续剖析与 Metrics、Tracing 关联?从异常火焰图下钻到具体请求与代码行的完整排查路径是什么?

  • 持续剖析与 Metrics/Tracing 关联
  • 下钻排查路径
  • 标签关联

持续剖析(Continuous Profiling)通过采集的 profile 数据(按服务/实例/时间/标签)与 Metrics、Tracing 关联,形成完整排查路径。完整路径:1) 从 Metrics 告警发现异常(如延迟上升、错误率升高);2) 通过 Tracing 定位到具体服务与调用链(traceId、spanId);3) 从持续剖析的火焰图中,按时间/标签对应到异常时段,查看该时段的 CPU/分配/锁相位图,定位热点函数;4) 从火焰图下钻到具体代码行(含源码定位),结合日志确认根因。关联的关键是统一的标签(服务名、实例、时间戳)与 traceId,使"指标→链路→火焰图→代码行"可下钻。这实现了从"宏观告警"到"微观代码"的完整诊断。

关联三者的核心是统一的标签与时间对齐。持续剖析让火焰图成为可回溯的"性能快照",支撑从指标告警到代码行的完整定位。

#
★★

50. 容器与 cgroup 环境下剖析工具读取 CPU 配额、内核符号(perf_event)会遇到哪些权限与精度问题

容器与 cgroup 环境下剖析工具读取 CPU 配额、内核符号(perf_event)会遇到哪些权限与精度问题?

  • cgroup CPU 配额与剖面
  • perf_event 权限
  • 精度问题

容器/cgroup 环境下剖析工具面临的问题:1) 权限:读取 perf_event 需要 perf_event_paranoid <= 1 或 CAP_SYS_ADMIN/CAP_PERFMON,容器默认可能受限,需提权或调整 sysctl;2) CPU 配额:cgroup 的 CPU 限制(quota)与系统 CPU 不一致,剖析基于系统 CPU 频率采样,可能无法反映容器内实际 CPU 配额,导致火焰图比例偏差;3) 内核符号:读取内核符号(如内核栈)需要 root 权限或 CAP_SYS_ADMIN,容器内可能无法完整解析内核栈;4) 宿主/容器 CPU 视图:容器看到的 CPU 数可能是宿主全部,需按 cgroup 配额解释;5) 采样精度:虚拟化/容器调度可能引入采样偏差。解决:使用容器感知的剖析(按 cgroup 限流)、提升权限、配合 cgroup 指标校准。

容器环境剖析需处理权限与 CPU 配额校准问题。理解 cgroup 与 perf 的交互,才能获得准确的容器内性能数据。

#
★★

51. 剖析 Agent 在虚拟线程(Virtual Threads)场景下的兼容性边界,载体线程视角会带来怎样的误读

剖析 Agent 在虚拟线程(Virtual Threads)场景下的兼容性边界是什么?载体线程视角会带来怎样的误读?

  • 虚拟线程与载体线程
  • 剖析的兼容性
  • 载体线程视角的误读

虚拟线程(Virtual Threads)由 JVM 调度到载体线程(Carrier Thread,平台线程)上执行,剖析 Agent 在虚拟线程场景下面临兼容性边界:1) 采样看到的是载体线程的栈,可能无法区分虚拟线程,导致同一载体线程上多个虚拟线程的样本混淆;2) 若剖析工具只按载体线程关联样本,会把多个虚拟线程的 CPU 时间混在一起,造成误读(无法定位是哪个虚拟线程/任务在烧 CPU);3) 虚拟线程的挂起/恢复不消耗载体线程 CPU,但 wall-clock 采样需正确识别虚拟线程的当前任务。JFR 对虚拟线程有专门支持(jdk.VirtualThreadStart 等事件),async-profiler 新版也支持虚拟线程栈。误读风险:载体线程视角高估某个虚拟线程的 CPU 占比,或把虚拟线程的等待误判为阻塞。

虚拟线程剖析需按虚拟线程维度关联样本,而非仅载体线程。理解载体线程视角的局限,才能避免性能误判。

#

52. 性能测试(JMeter/Gatling)的边界

JMeter/Gatling 等性能测试工具的边界是什么?

  • 性能测试工具的能力
  • 适用场景
  • 局限

JMeter/Gatling 是流行的性能压测工具:JMeter 基于线程模型、图形界面、脚本灵活、生态大;Gatling 基于 Scala DSL、异步高并发、报告丰富。边界:它们主要验证"系统在特定负载下的吞吐与延迟"(容量/性能),但压测结果受测试环境(硬件、网络、生产差异)影响,不能直接等同于生产容量;且压测工具本身可能成为瓶颈(单机线程数限制)。适用场景:基线压测、容量评估、性能回归、并发验证。局限:无法完全模拟真实用户行为与数据分布,需结合生产监控与全链路压测校准。边界是"压测是近似,需换算与校准"。

压测工具提供受控负载,但结果需结合环境差异换算。理解其边界(模拟 vs 真实)是正确解读压测结果的基石。

#

53. 测试数据构造的 Test Data Builder 与 Object Mother 模式如何组织,Instancio 等自动填充库如何减少样板代码

Test Data Builder 与 Object Mother 模式如何组织测试数据?Instancio 等自动填充库如何减少样板代码?

  • Test Data Builder 模式
  • Object Mother 模式
  • Instancio 自动填充

Test Data Builder 模式用 Builder 逐字段构造测试对象,只设置关心的字段,其余用默认值,提升可读性。Object Mother 模式提供集中化的测试对象工厂(如 TestUsers.validUser()),复用预设的对象,减少重复。Instancio 是自动填充库,通过 Instancio.create(User.class) 自动生成填充所有字段的对象,支持 set/ignore 定制、集合生成、泛型处理,极大减少手动构造样板代码,适合快速生成大量测试数据。组织上:简单数据用 Instancio,需要特定业务语义的用 Builder/Object Mother 预设。取舍:Instancio 便捷但可能掩盖字段语义,Builder 明确但样板多。

测试数据构造是测试可维护性的关键。Instancio 自动化填充降低样板,Builder/Object Mother 提供语义化构造,需按需组合。

#

54. 采样式剖析(sampling profiling)的工作原理是什么,采样频率与统计精度、运行开销之间如何权衡

采样式剖析(sampling profiling)的工作原理是什么?采样频率与统计精度、运行开销之间如何权衡?

  • 采样剖析原理
  • 采样频率与精度
  • 开销权衡

采样式剖析定期(如每 N 毫秒)对运行线程的调用栈做快照,统计各方法被采样到的次数,按比例近似其耗时占比。原理是"抽样统计":样本越多,比例越接近真实分布。采样频率与精度、开销权衡:频率越高,样本越多、统计精度越高(能捕捉更短的方法),但开销越大(中断、栈快照成本);频率越低,开销小但精度低、难以捕捉短方法。工程上 CPU 采样常用 10-100Hz,wall-clock 采样同理。权衡目标是"在可接受开销下获得足够精度"。采样剖析无法精确捕捉极短方法(缺失),但整体分布可靠。

采样剖析以"近似"换取"低开销",是生产环境性能分析的首选。理解采样频率与精度的权衡,才能合理设置采样参数。

#

55. @Testcontainers 的工程应用

@Testcontainers 注解在工程中如何应用?

  • @Testcontainers 注解
  • @Container 启动
  • 自动管理容器生命周期

@Testcontainers 是 Testcontainers 的 JUnit 5 扩展注解,标注在测试类上,自动管理容器生命周期(测试前启动、测试后停止)。配合 @Container 标注容器字段,容器在测试类开始时启动,结束时停止。用法:@Testcontainers class Test { @Container static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:15"); }。也可用 @Testcontainers(disabledWithoutDocker = true) 在无 Docker 时跳过。工程价值:自动化的容器生命周期管理,让集成测试使用真实中间件,简化配置。

@Testcontainers 注解提供声明式容器管理,配合 @Container 无需手动启停。理解静态/实例容器字段的区别是使用关键。

#

56. ArchUnit(架构测试)的工程应用

ArchUnit 在工程中如何用于架构测试?

  • ArchUnit 的作用
  • 架构规则定义
  • 分层/依赖约束

ArchUnit 是 Java 架构测试库,用代码断言架构规则,防止架构腐化。它通过字节码分析类/包依赖,检查分层、依赖方向、命名、循环依赖等。示例:layeredArchitecture().layer("Controller").definedBy("..controller..").layer("Service").definedBy("..service..").whereLayer("Controller").mayNotBeAccessedByAnyLayer()classes().that().resideInAPackage("..service..").should().onlyBeAccessed().byClassesThat().resideInAnyPackage("..controller..", "..service..")。工程价值:把架构约束纳入 CI,违反架构的代码无法合入,保障架构长期稳定。适合分层架构、依赖倒置、禁止循环依赖等治理。

ArchUnit 把架构规则"可执行化",在 CI 中拦截违规。理解分层与依赖断言是核心,是架构治理的自动化手段。

#

57. JFR(Java Flight Recorder)与 async-profiler 在生产环境的开销、精度与接入成本上有何差异

JFR 与 async-profiler 在生产环境的开销、精度与接入成本上有何差异?

  • JFR 与 async-profiler 的原理
  • 开销与精度
  • 接入成本

JFR(Java Flight Recorder)是 JDK 内置的剖析工具,基于事件(JVM 事件、线程、GC、分配等),开销低(通常 <1-2%),随 JDK 分发热部署,接入成本低(java -XX:+FlightRecorder 或 JFR.start 命令),精度较高、覆盖 JVM 内部事件(GC、JIT、锁),但采样粒度受事件配置影响。async-profiler 是开源的采样剖析器,基于 perf_event 或 JVMTI 采样,精度高(可到 10Hz-100Hz 采样,捕捉短方法),开销可调但通常略高于 JFR,需单独安装(native 库),接入成本略高(需容器挂载、权限)。差异:JFR 事件驱动、自带、低开销、覆盖 JVM 内部;async-profiler 采样驱动、高精度、需安装、适合火焰图。生产常用 JFR 持续录制 + async-profiler 按需深挖。

JFR 与 async-profiler 互补:JFR "自带低开销、覆盖 JVM 内部",async-profiler "高精度火焰图"。选择取决于精度需求与部署灵活性。

#

58. Testcontainers 与 localstack 的协作

Testcontainers 与 localstack 如何协作?

  • Testcontainers 管道
  • localstack 容器
  • 组合使用

Testcontainers 与 localstack 协作:通过 Testcontainers 启动 localstack 容器(localstack/localstack 镜像),模拟 AWS 服务,供依赖 AWS 的测试使用。localstack 提供 LocalStackContainer 专用容器,Testcontainers 管理其生命周期与端口、region 等配置。用法:@Container static LocalStackContainer localstack = new LocalStackContainer(DockerImageName.parse("localstack/localstack")).withServices(S3, SQS); 然后通过 localstack.getEndpointConfiguration() 配置 AWS SDK 客户端。协作价值:Testcontainers 提供 Docker 容器管理,localstack 提供 AWS 模拟,两者结合让 AWS 依赖的集成测试无需真实 AWS 账号、可重复执行。

Testcontainers 管理容器生命周期,localstack 模拟 AWS 服务,组合实现 AWS 依赖的本地集成测试。理解服务配置与 endpoint 注入是核心。

#

59. Testcontainers(Docker 容器化测试)

Testcontainers 的 Docker 容器化测试原理是什么?

  • Testcontainers 原理
  • Docker 容器生命周期
  • 集成测试应用

Testcontainers 通过 Docker 启动真实中间件容器(数据库、消息队列、浏览器等),供集成测试使用,测试结束后销毁容器。原理:测试用例中启动容器(如 new PostgreSQLContainer("postgres:15")),Docker 创建容器,测试通过容器端口与内部服务交互,测试结束容器自动停止删除。它保证测试环境与生产环境一致(真实版本、真实行为),避免 embedded 与生产差异。配合 @DynamicPropertySource 注入端口。工程价值:测试可信度高的集成测试,代价是需要 Docker 环境、启动较慢。

Testcontainers 的核心价值是"用真实容器消除环境差异"。理解容器生命周期管理与端口注入是使用关键。

#

60. WireMock 的 HTTP mock(/__admin/mappings)

WireMock 的 HTTP mock(/__admin/mappings)如何工作?

  • WireMock 管理接口
  • /__admin/mappings
  • 动态管理 stub

WireMock 提供管理 API /__admin/mappings,用于动态添加、查询、删除 stub 映射(mappings),而不必在代码中静态定义。可通过 POST /__admin/mappings 添加 JSON 格式的 stub(request 匹配 + response),GET 查询现有 stub,DELETE 删除。示例:POST /__admin/mappings body {"request":{"method":"GET","url":"/api/x"},"response":{"status":200,"jsonBody":{...}}}。这便于在测试中动态注册/调整 stub,或与测试框架集成。工程价值:动态管理 HTTP mock,灵活控制测试中下游服务的响应。

/__admin/mappings 是 WireMock 的动态管理入口,让 stub 可在运行时动态维护。理解 JSON 格式与 HTTP 操作是核心。

#

61. async-profiler 支持的事件类型(cpu/alloc/lock/wall)分别采集什么信号,各自适合定位哪类性能问题

async-profiler 支持的 cpu/alloc/lock/wall 事件类型分别采集什么信号?各自适合定位哪类性能问题?

  • 各事件类型采集的信号
  • 对应的性能问题
  • 选型

async-profiler 支持多种事件:cpu 事件采集线程在 CPU 上执行时的栈,适合定位 CPU 热点(CPU 密集型);alloc 事件采集对象分配点,适合定位内存分配热点(高分配、GC 压力);lock 事件采集锁竞争/等待,适合定位锁争用与阻塞;wall 事件采集所有线程的栈(无论是否在 CPU 上),适合定位 I/O 等待、sleep、阻塞等 wall-clock 时间。选型:CPU 拉满用 cpu,频繁 GC/分配高用 alloc,线程阻塞/锁竞争用 lock 或 wall,I/O 等待用 wall。不同事件类型针对不同性能问题。

事件类型决定剖析视角。选择正确的事件类型才能定位对应问题,这是 async-profiler 使用的基本原则。

#

62. 契约测试与 Pact Broker 的协作

契约测试与 Pact Broker 如何协作?

  • 契约测试(Pact)
  • Pact Broker 的作用
  • 消费者/生产者协作

Pact 是契约测试工具,消费者(Consumer)定义期望的接口契约(Pact 文件),生产者(Provider)验证是否满足契约。Pact Broker 是集中存储与服务 Pact 的仓库,用于发布、共享、管理契约文件,并支持消费者/生产者版本匹配、触发验证(如消费者发布新契约后通知生产者验证)。协作流程:消费者测试生成 Pact 文件 → 发布到 Pact Broker → 生产者根据 Broker 中的契约运行验证测试 → 结果回传 Broker。Pact Broker 提供版本管理与自动化验证,支持"消费者驱动的契约"与"can-i-deploy"门禁。工程价值:跨服务契约的集中管理与自动化验证,防止接口不兼容。

Pact Broker 是契约测试的枢纽,实现契约的集中管理与消费者/生产者的自动验证。理解其发布与验证流程是核心。