单元与端到端测试

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

1. 快照测试(Snapshot Testing)的适用场景与陷阱

快照测试(Snapshot Testing)适用于哪些场景?它在实践中存在哪些陷阱,应如何规避?

  • 快照测试的适用场景:UI 输出与序列化结果防回归
  • 快照的脆弱性与"盲目更新"陷阱
  • 快照与代码评审、更新规范等治理手段的配合

快照测试将组件渲染输出或函数序列化结果与磁盘上保存的快照逐字比对,当输出变化时测试失败并生成 diff。适用场景包括:防止 UI 结构与文本内容的意外回归、跟踪配置对象与序列化数据结构的变更、在重构后快速确认输出未发生变化。它的本质是"变更检测器"而非"正确性证明"——即使输出变化是预期的,快照比对依然会失败。

主要陷阱有三类:一是快照体积庞大且难以审查,开发者往往不逐行阅读差异;二是失败时直接以 -u 盲目更新快照,可能掩盖真实回归;三是快照与实现细节(DOM 结构、className)耦合过紧,导致频繁误报与维护负担。规避方式:控制快照粒度(只快照有意义的输出)、将快照更新纳入代码评审流程、对高频变化区域改用针对性断言、为快照补充语义断言而非只依赖全文比对。

本题考察对快照测试本质的理解:快照擅长捕获变更,不擅长验证正确性。回答应点明适用场景(防回归)与核心陷阱(盲目 -u、审查困难、实现耦合),并给出治理手段,体现对测试可维护性的工程认知。

#
★★★

2. Mock(jest.mock、vi.mock)与 Stub、Spy 的边界

jest.mock / vi.mock 与 Stub、Spy 的边界在哪里?过度使用 Mock 会带来什么问题?

  • mock 的模块级替换与 hoisting 机制
  • stub 固定返回值与 spy 记录调用的区别
  • 过度 mock 导致测试与实现耦合的风险

Mock(jest.mock/vi.mock)在模块层面整体替换依赖,适合消除模块副作用(网络请求、定时器、原生 API);Stub 指用固定返回值替代真实实现,关注"返回什么";Spy(vi.spyOn/jest.spyOn)可以保留原实现或替换实现,同时记录调用次数、参数与返回值,关注"如何被调用"。三者边界:mock 管模块边界,stub 管返回值,spy 管行为验证。

过度 Mock 的代价:测试断言的是 mock 实现而非真实行为,依赖内部实现一旦调整,测试便大面积失效;同时会掩盖真实集成问题,形成虚假的安全感。最佳实践是"由外到内":优先使用真实依赖与集成测试,仅在模块边界(网络、时间、环境)处做 mock,并用 spy 验证关键交互,保持测试与被测行为的贴近。

本题考察测试替身(Test Double)的精确分类与边界:mock 是模块级替换、stub 是返回值替身、spy 是行为记录器。回答还应点出过度 mock 的耦合风险与"只在边界 mock"的原则,展现工程判断力。

import { vi } from 'vitest'
const spy = vi.spyOn(api, 'fetchList').mockResolvedValue([{ id: 1 }])
expect(spy).toHaveBeenCalledWith({ page: 1 })
#
★★★

3. Chromatic 与 a11y/性能/契约测试的协作

Chromatic 如何与 a11y、性能与契约测试协作,共同构成组件质量体系?

  • Chromatic 的视觉回归、交互测试与 a11y 快照能力
  • 与 axe-core 无障碍检查的集成
  • 与性能预算、契约测试的分层协作

Chromatic 基于 Storybook 运行:为每个 story 生成多浏览器快照并与基线比对,捕获视觉回归;同时支持交互测试(play 函数)、a11y 快照(集成 axe-core,自动检测对比度、ARIA 等问题)与 TurboSnap(按依赖变更只测试相关 story),把"视觉正确性"的验证前移到 PR 阶段。

协作上形成分层体系:Chromatic 负责组件层的视觉与可访问性回归,axe-core 补充 WCAG 规则级深度检查,Lighthouse CI 负责页面级性能预算,契约测试(Pact/OpenAPI)负责前后端接口一致性——视觉、无障碍、性能、契约四类风险各由专门工具治理,Chromatic 作为组件层的"变更门禁"在其中承上启下,将不同质量维度编织进同一条 CI 流水线。

考察组件质量体系的分工思想:视觉回归(Chromatic)、无障碍(axe)、性能(Lighthouse)、契约(Pact)各自解决一类风险,回答应体现分层协作而非工具罗列。

#
★★★

4. MSW 在 CI/CD 与并行执行的工程价值

MSW 在 CI/CD 与并行执行中如何保证测试的确定性与隔离性?

  • MSW 浏览器 Service Worker 与 Node 拦截双端原理
  • CI 中去网络依赖的确定性收益
  • 并行执行下的 handler 隔离与状态重置

MSW(Mock Service Worker)在浏览器端通过 Service Worker 拦截网络请求,在 Node 端通过 @mswjs/interceptors 拦截底层请求,实现同一套 handler 双端复用。CI 中它让测试完全脱离真实网络,消除延迟抖动、限流与后端不可用导致的 flaky,同时无需修改业务代码,是"测试替身置于网络层"的典型实践。

并行执行时,每个 worker 进程运行不同测试文件,MSW 按测试文件注册 handler,并通过 beforeAll 注册、afterEach 重置(server.resetHandlers())保证用例间隔离;配合每个 worker 独立的数据库、端口与浏览器上下文,可保证并行下的确定性。结合 CI 的 sharding 与缓存策略,MSW 使 E2E 与单测的 mock 描述统一,显著降低维护成本。

考察 MSW 的双端拦截原理与 CI 工程价值:确定性(去网络)、一致性(同一 handler 双端复用)、隔离性(按文件注册与重置)。回答应突出"网络层 mock"的层次思想。

#
★★★

5. Vitest 2.x in-source testing 与 type testing 的工程价值

Vitest 2.x 的 in-source testing 与 type testing 如何提升测试的工程价值?

  • import.meta.vitest 与源码内测试机制
  • expectTypeOf/typecheck 类型测试
  • 覆盖率真实性与构建产物排除

in-source testing 允许把测试写在源码文件内,通过 if (import.meta.vitest) 条件块声明测试,使单测紧邻实现,降低上下文切换;Vitest 2.x 在此基础上完善了 typecheck 集成:通过 vitest typecheck 运行类型测试,用 expectTypeOf/assertType 对导出类型做编译期断言,覆盖泛型推导、类型收窄等运行时无法验证的契约。

工程价值体现在三方面:覆盖率更真实(源码内测试计入实现文件,不再因测试文件稀释)、类型契约可执行(重构时类型错误提前暴露)、测试与实现同步演进。注意配置 include 与 define(import.meta.vitest)以便在构建时剥离测试代码,避免生产产物膨胀。

本题考察 Vitest 2.x 的差异化能力:源码内测试(位置内聚)与类型测试(编译期验证)。回答应强调"类型即测试"的新维度与构建剥离的配套治理。

export const add = (a: number, b: number) => a + b
if (import.meta.vitest) {
  const { it, expect } = import.meta.vitest
  it('adds', () => expect(add(1, 2)).toBe(3))
}
#
★★★

6. 覆盖率报告(v8 provider)coverage.exclude 与阈值(thresholds)

使用 v8 provider 时,coverage.exclude 与 thresholds 如何配置以建立覆盖率门禁?

  • v8 provider 与 Istanbul provider 的原理差异
  • coverage.exclude 排除规则的作用
  • thresholds 全局与每文件阈值及 CI 阻断

Vitest 的 coverage.v8 基于 V8 原生覆盖率数据(执行代码行计数),比 Istanbul 的转译插桩更快更准,但要求 Node 18+。coverage.exclude 通过 glob 排除无需统计的文件(生成物、类型声明、测试文件自身、配置文件),避免稀释真实覆盖率;也可用 include 白名单限定统计范围,让指标聚焦业务代码。

thresholds 支持全局(lines/functions/branches/statements)与每文件(perFile)两种维度,未达标时测试失败,从而在 CI 中形成覆盖率门禁。实践中应先采集基线再逐步提高阈值,配合 branch 与 statements 维度防止"只堆行覆盖",并把覆盖率报告(lcov/html)作为 CI artifact 供团队审阅。

考察覆盖率治理的两个关键配置:exclude 决定"统计什么",thresholds 决定"要求多少"。回答应提到 v8 provider 原理、exclude 常见对象与 thresholds 的 CI 门禁作用。

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      exclude: ['src/types/**', 'dist/**', '**/*.d.ts'],
      thresholds: { lines: 80, functions: 80, branches: 75, statements: 80 }
    }
  }
})
#
★★★

7. Playwright v1.61 Trace Viewer 与 trace.zip 调试的工程价值

Playwright v1.61 的 Trace Viewer 与 trace.zip 如何提升失败用例的调试效率?

  • trace 录制的内容:操作快照、网络、控制台与源码映射
  • trace.zip 的保存与跨环境共享机制
  • 时间轴回放与快照检查的定位流程

Playwright trace 记录测试执行全过程:每一步操作前后的 DOM 快照、网络请求与响应、控制台消息、页面错误与源码位置映射,并以时间轴呈现。v1.61 的 Trace Viewer 支持暂停、跳步、检查任意快照元素与请求详情,把"失败后回放现场"变成标准工作流,替代过去依赖日志猜测的调试方式。

trace.zip 将 trace 压缩为单文件,测试失败时可自动(--trace on-first-retry)或手动保存,随 CI artifact 上传;开发者在本地或 trace.playwright.dev 打开即可离线分析,无需复现环境。工程价值:大幅缩短 flaky 与偶发失败的定位成本,尤其适合 CI 与本地环境不一致的场景;配合网络面板可同时排查接口层问题,实现端到端的一站式排障。

考察 Playwright 调试链路的现代实践:trace 是"可回放的现场记录",zip 解决跨环境共享,时间轴加快照加网络把调试从"猜"变为"看"。回答应突出其相对传统日志的优势。

#
★★★

8. Playwright 的 page.locator() 用户视角选择器与 Web-First Assertions

Playwright 的 page.locator() 用户视角选择器与 Web-First Assertions 解决了什么问题?

  • locator 与 getBy* 用户视角查询体系
  • Web-First 断言的自动等待与重试机制
  • strict mode 与选择器脆弱性治理

page.locator() 是 Playwright 的元素定位核心,推荐使用 getByRole、getByText、getByLabel 等用户视角选择器:按角色、可见文本、可访问名称定位元素,与真实用户感知一致,天然符合可访问性实践,避免 CSS 类名或 DOM 结构调整导致的选择器脆弱。locator 支持链式查询(locator('row').getByRole('button'))与过滤(filter({ hasText })),并会自动等待元素出现与可操作。

Web-First Assertions(expect(locator).toBeVisible()、toHaveText() 等)在断言前自动重试直至超时,替代手写 waitForTimeout 与轮询循环,把"等待与断言"合并为声明式表达,显著降低时序类 flaky。配合 strict mode,Playwright 在选择器命中多个元素时会直接报错,促使开发者编写更精确的定位器,从源头治理脆弱测试。

本题考察 Playwright 的核心设计理念:用户视角定位(语义、可访问性)与声明式自动等待(消除手写等待)。回答应体现"定位器 + Web-First 断言"如何系统性降低 E2E 的脆弱性。

await expect(page.getByRole('button', { name: '提交' })).toBeEnabled()
await page.getByLabel('用户名').fill('alice')
#
★★★

9. Cypress 与 Playwright 的能力对比

Cypress 与 Playwright 在架构与能力上有哪些差异?工程上应如何选型?

  • Cypress 浏览器内代理架构与命令队列机制
  • Playwright 多浏览器、多上下文与并行架构
  • 选型维度:跨浏览器、并行、调试体验与团队技能

Cypress 在浏览器内运行测试(与页面同进程代理),提供命令队列自动重试、时间旅行调试(DOM 快照回放)、交互式 Runner 与网络 stub(cy.intercept),但历史上受同源策略限制,多标签页与跨域支持较弱,并行执行依赖 Dashboard 服务。Playwright 通过 CDP/WebDriver BiDi 协议驱动浏览器,原生支持 Chromium/WebKit/Firefox 三引擎、多页面多上下文(多登录态并行)与 worker 级免费并行。

选型视角:需要三浏览器兼容矩阵与高并行 CI 时 Playwright 占优;重视交互式调试体验、命令语义直观且团队已熟悉 Cypress 时它也有强项;两者均支持组件测试。实践中常以 Playwright 承担 E2E 主框架,Cypress 保留在既有团队或特定场景,核心是匹配团队的维护能力与测试金字塔需求,而非单纯比较功能数量。

考察两大 E2E 框架的架构本质差异:Cypress 同进程代理 vs Playwright 独立协议驱动,并落实到跨浏览器、并行、调试三大选型维度。

#
★★★

10. Vue Test Utils 的组件挂载与 props 注入

Vue Test Utils 如何挂载组件并注入 props?与 Testing Library 的用法有何差异?

  • mount/shallowMount 的挂载深度差异
  • props 与 global 配置(plugins、stubs、mocks)的注入
  • 与 Testing Library 查询哲学的差异

Vue Test Utils(VTU)通过 mount(Component, options) 完整挂载组件(含子组件与生命周期),shallowMount 只渲染当前组件、以 stub 替代子组件,用于隔离单测。props 通过 options.props 注入(如 mount(Comp, { props: { items: [] } })),也可用 setProps 在测试中更新;global 选项可注入 plugins、directives、mocks(如 $router)与 stubs,配合 vi.fn() 模拟事件与外部依赖。

与 Testing Library 的差异:VTU 面向组件实例与内部结构(wrapper.vm、find 查询),Testing Library 强调用户视角查询(getByRole/getByText)且不测实现细节。工程上常用 VTU 做 Vue 组件逻辑测试,用 Testing Library 风格的"行为测试"互补,避免断言 DOM 实现细节导致测试脆弱,两者各司其职。

考察 Vue 官方测试工具的挂载 API(mount/shallowMount、props、global)及其与 Testing Library 测试哲学的差异——组件内部结构 vs 用户视角行为。

import { mount } from '@vue/test-utils'
const wrapper = mount(MyList, { props: { items: ['a'] }, global: { plugins: [pinia] } })
expect(wrapper.text()).toContain('a')
#
★★★

11. Vitest 1.x vi.mock/vi.useFakeTimers 在模块副作用/时间相关单测的工程价值

vi.mock 与 vi.useFakeTimers 在模块副作用消除与时间相关单测中如何发挥作用?

  • vi.mock 的模块级替换与 hoisting 机制
  • vi.useFakeTimers 对时间 API 的接管
  • 定时器推进、恢复与清理实践

vi.mock(path, factory) 在模块加载前替换依赖(Vitest 自动 hoist 到文件顶部),适合消除网络请求、环境 API、第三方 SDK 等模块副作用,让被测单元在确定性环境中运行;配合 vi.fn() 工厂可断言依赖被如何调用。vi.useFakeTimers() 接管 setTimeout/setInterval/Date/queueMicrotask 等时间 API,使时间完全可控:vi.advanceTimersByTime(ms) 推进时间触发回调、vi.runAllTimers() 跑完全部定时器,测试结束后用 vi.useRealTimers() 恢复。

两者结合的价值:对"倒计时、防抖、轮询、超时重试"等时间逻辑,不再依赖真实等待(慢且 flaky),而是虚拟推进时间并断言行为。注意在 afterEach 中清理(vi.useRealTimers()/vi.restoreAllMocks())避免跨用例污染;定时器回调内部包含异步操作时,需配合 vi.advanceTimersByTimeAsync 处理微任务队列。

考察 Vitest 时间控制的两大工具:模块 mock 消除副作用、fake timers 虚拟化时间。回答应包含推进/恢复的 API 与清理实践,体现确定性测试思维。

vi.useFakeTimers()
vi.advanceTimersByTime(3000)
expect(spy).toHaveBeenCalledTimes(1)
vi.useRealTimers()
#
★★★

12. Testing Library 的用户视角查询与可访问性测试

Testing Library 的用户视角查询如何服务于可访问性测试?

  • 查询优先级:getByRole/getByLabelText/getByText 等
  • 从用户视角而非实现细节断言
  • 与 jest-axe/axe-core 的互补关系

Testing Library 的核心思想是"像用户一样查询":优先使用 getByRole(按 ARIA 角色)、getByLabelText(表单标签)、getByPlaceholderText、getByText、getByTitle 等语义查询,避免 test id 与 CSS 类名;查询方式直接暴露可访问性问题——例如 getByLabelText 找不到元素,往往意味着 label 未与控件正确关联。同时提供 screen.debug、within 与 findBy* 异步查询提升可用性。

在可访问性上,Testing Library 与 jest-axe/axe-core 互补:前者保证"查询路径符合用户感知",后者做 WCAG 规则级检查(对比度、ARIA 用法、landmark 结构)。两者结合使可访问性从"事后审计"变为"测试即护栏",任何破坏 label 关联或角色语义的改动都会在 CI 中失败,形成持续的可访问性保障。

考察 Testing Library 的设计哲学:查询方式本身就是可访问性实践,语义查询(role/label/text)让测试自动贴近用户与辅助技术视角,并配合 axe 做规则级检查。

#
★★★

13. Happy DOM 与 jsdom 的取舍 与 Playwright 1.5x 的现代取舍

Happy DOM 与 jsdom 各有哪些取舍?结合 Playwright 1.5x 应如何构建现代 DOM 测试环境?

  • jsdom 的生态成熟度与兼容性
  • happy-dom 的速度与现代 API 支持
  • Playwright Browser Mode 作为真实浏览器第三极

jsdom 是历史最久的 Node DOM 模拟器,兼容性好、生态广泛,但实现不完整且性能一般,布局与渲染基本是仿真;happy-dom 以更现代的实现提供更快执行与更全的 DOM/HTML API(如 Web Components、shadow DOM 支持更好),但某些边缘行为与 jsdom 不一致,依赖 jsdom 特定行为的库需要验证。两者的共同局限是:没有真实布局引擎、事件与 CSS。

Playwright 1.5x 的 Browser Mode 让 Vitest 在真实 Chromium/WebKit/Firefox 中运行测试,获得真实布局、事件与浏览器 API,把"DOM 模拟器 vs 真实浏览器"变成光谱选择:纯逻辑与简单 DOM 用例用 happy-dom/jsdom(快、省资源),涉及布局、真实交互与浏览器特性的用例用 Browser Mode(真实)。取舍要点:速度、API 完整性、与项目依赖的兼容性、CI 资源成本。

考察 DOM 测试环境的选型光谱:jsdom(兼容)、happy-dom(快)、真实浏览器(真)。回答应结合项目依赖兼容性与性能需求给出分层选择。

#
★★★

14. Cypress Component Testing 在真实组件挂载与真实浏览器执行

Cypress Component Testing 如何在真实浏览器中挂载组件?相较纯单测有何价值?

  • mount 命令在真实浏览器中的组件挂载
  • 真实事件、CSS 与网络 stub 能力
  • 与 E2E 复用及测试金字塔分工

Cypress Component Testing 通过 mount() 命令在真实浏览器(Cypress Runner)中挂载组件,测试代码与组件同页执行,支持传入 props、模拟事件、stub 网络(cy.intercept)以及注入 store 与 plugins;配置层面支持 Vite 与 Webpack 构建器,还可将 Storybook story 直接作为测试入口。由于运行在真实浏览器,CSS 生效、事件真实派发、网络真实可控,能发现 jsdom 无法暴露的布局与浏览器 API 问题。

相较纯单测的价值:所见即所得的调试体验(Runner 中可视化组件、点击查看 DOM 快照、时间旅行)、更真实的集成度(组件加样式加浏览器 API),且测试代码风格与 E2E 一致、可复用 Cypress 生态命令;代价是速度慢于 jsdom 单测。工程上建议按金字塔分工:纯逻辑用 Vitest/Jest 快跑,组件级交互与视觉用 Cypress Component Testing 或 Storybook 交互测试覆盖。

考察组件测试在真实浏览器中的价值与定位:真实渲染、真实事件、可调试,是单元测试与 E2E 之间的中间层;回答应给出金字塔分工建议。

#
★★★

15. Cypress 14 与 MSW 的 API 拦截协作

Cypress 14 如何与 MSW 协作进行 API 拦截?两者共存的边界在哪里?

  • MSW 在浏览器中的 Service Worker 拦截原理
  • 与 cy.intercept 的并存与优先级协调
  • worker 注册时机与 handler 一致性维护

MSW 在浏览器端通过注册的 Service Worker 拦截网络请求,与 Cypress 运行在同一个真实浏览器中,因此可以协作:由 MSW 提供接口层的 handler 描述(统一浏览器与 Node 双端),Cypress 负责用户交互与断言。使用时需在测试初始化阶段等待 worker 注册完成(如 Cypress 任务或 before 钩子中 await worker.start()),避免请求在注册前发出而未被拦截。

与 cy.intercept 的边界:cy.intercept 在 Cypress 代理层拦截,适合测试内临时 stub;MSW 的 handler 是模块级声明,适合跨测试复用的接口契约。两者并存时注意优先级与覆盖范围,避免同一请求被双层拦截产生混乱。工程上建议:统一的 API mock 描述放在 MSW handler 中,测试内特殊场景用 cy.intercept 做局部覆盖,并保持 handler 与契约测试(Pact/OpenAPI)数据一致。

考察 Cypress 与 MSW 协作的工程细节:同浏览器双工具共存,关键是 worker 注册时机与 handler 复用,同时要明确与 cy.intercept 的分工边界。

#
★★★

16. Storybook 8+ Vitest plugin 在组件故事与单测融合的工程价值

Storybook 8+ 的 Vitest plugin 如何把组件故事与单元测试融合?其工程价值是什么?

  • story 自动转换为 Vitest 测试用例
  • 组件测试与 play 函数协作
  • 测试基础设施复用的工程收益

Storybook 8+ 的 Vitest plugin 将每个 story 自动生成对应的 Vitest 测试:在测试运行器(默认 jsdom/happy-dom,可配置浏览器)中挂载 story,执行其 play 函数并允许补充断言,实现"组件故事即测试用例"。开发者无需为每个组件单独编写测试脚手架,story 的 args、装饰器与 play 交互全部复用,测试与组件文档、视觉回归共享同一份描述。

工程价值:消灭"故事与测试两套代码"的重复维护,组件行为变更只需改一处;story 的 props 矩阵天然成为测试输入矩阵,提升覆盖率;配合 CI 并行执行,组件级回归成本大幅下降。边界:story 侧重于渲染与交互状态,业务逻辑断言仍需在测试中显式补充,两者互补而非替代。

考察 Storybook 与单测的融合趋势:CSF 描述即测试资产,Vitest plugin 让 story 直接产出可执行测试,回答应强调"单一描述源"与并行收益。

#
★★★

17. axe-core 的可访问性自动化测试

axe-core 如何实现可访问性的自动化测试?其能力边界在哪里?

  • axe-core 的规则引擎与注入运行方式
  • 与 jest-axe、Playwright/Cypress 的集成
  • 自动化无法覆盖的可访问性问题

axe-core 是 Deque 开源的 WCAG 规则引擎:将脚本注入页面运行(axe.run()),按规则检查 DOM 的可访问性树,返回 violations(违规描述、节点、修复建议)、passes 与 incomplete(需人工判断)三部分结果。集成方式多样:jest-axe(jest/vitest 中 toHaveNoViolations)、Playwright 与 Cypress 的 axe 插件,均可把检查嵌入现有测试与 CI。

能力边界:axe 覆盖约 50% 的 WCAG 检查点,擅长结构性问题(对比度、ARIA 用法、label 关联、landmark),但无法验证"键盘可操作性是否顺畅""屏幕阅读器实际朗读顺序"等体验性问题,这些需要人工测试与真实用户反馈。最佳实践是"自动化规则 + incomplete 人工复核 + 定期真人可用性测试"三层组合。

考察 axe-core 的机制(规则引擎注入)与边界:自动化能拦截规则级问题,但体验级问题仍需人工,回答应体现自动化与人工的分层认知。

#
★★★

18. Vitest 与 Jest 在 ESM/TS/SWC 的工程取舍

Vitest 与 Jest 在 ESM、TypeScript 与 SWC 支持上有哪些差异?如何做工程取舍?

  • Jest 的 ESM 支持现状与配置复杂度
  • Vitest 基于 Vite 的原生 ESM 与 TS 处理
  • SWC/esbuild 转译速度与生态兼容

Jest 生态成熟、迁移资料丰富,但 ESM 支持长期依赖 Babel transform 与实验性配置(transform、extensionsToTreatAsEsm、jest-environment),TypeScript 需搭配 ts-jest(慢)或 babel-jest(无类型检查);SWC 可显著加速但需额外安装。Vitest 基于 Vite 构建,原生以 ESM 运行,TS 由 esbuild 转译(无类型检查),SWC 插件可进一步提升速度,watch 模式借助 Vite 的模块图实现毫秒级热更新。

工程取舍:新项目、全 ESM 或 Vite 技术栈优先 Vitest(配置少、快、原生 ESM);存量 Jest 项目迁移成本高时,可用 jest-rust/SWC 优化速度而保留 Jest。注意两者对"模块 mock 语义、hoisting、环境变量注入"的实现有细微差异,迁移时需逐项验证;无论选哪个,CI 中类型检查应作为独立步骤,避免依赖测试转译器做类型把关。

考察两大测试运行器的现代差异:ESM 一等公民(Vitest)vs 生态成熟(Jest),转译链路(esbuild/SWC vs Babel/ts-jest)决定速度,回答应给出迁移与选型建议。

#
★★★

19. vi.useFakeTimers 在定时器相关单元测试的精度控制

vi.useFakeTimers 如何实现对定时器相关单元测试的精度控制?

  • toFake 列表精确控制被接管的 API
  • setSystemTime 与 Date 控制
  • 推进策略:advanceTimersByTime 与 runAllTimers

vi.useFakeTimers({ toFake }) 允许精确指定接管哪些时间 API:默认接管 setTimeout、setInterval、clearTimeout、Date 等,也可仅接管其中一部分(如只控制 Date),避免过度模拟影响未接管的 API 行为;vi.setSystemTime(date) 直接设定系统时间,配合 fake timers 可稳定测试"基于时间的展示逻辑"(日期格式化、倒计时、过期判断)。

推进精度控制:advanceTimersByTime(ms) 按指定时长逐步推进并触发到期回调,适合断言"防抖 300ms 后只触发一次";runAllTimers() 无条件跑完所有定时器,适合断言"最终结果";runOnlyPendingTimers() 只跑当前挂起任务。异步场景使用 advanceTimersByTimeAsync 以推进微任务。核心原则:尽量模拟"真实时间流逝"而非"跳过全部时间",保证测试与生产行为一致。

考察 fake timers 的精细控制能力:toFake 决定接管范围、setSystemTime 控制时钟、不同推进 API 对应不同断言意图,回答应体现"选择性接管"的精度思维。

vi.useFakeTimers({ toFake: ['setTimeout', 'Date'] })
vi.setSystemTime(new Date('2026-08-04T12:00:00Z'))
vi.advanceTimersByTime(300)
#
★★★

20. Vitest Browser Mode(GA)

Vitest Browser Mode(GA)是什么?它解决了什么问题?

  • Browser Mode 在真实浏览器中运行测试
  • 与 jsdom/happy-dom 的对比
  • 配置方式与适用场景

Vitest Browser Mode 让 Vitest 测试运行在真实浏览器中(通过 Playwright 或 WebdriverIO provider 驱动 Chromium/WebKit/Firefox),支持单元测试、组件测试与 E2E 混合场景:每个测试在真实页面上下文中执行,获得真实 DOM、布局、事件与浏览器 API,而无需切换 jsdom/happy-dom 模拟器。测试代码仍由 Vite 转译,支持 HMR 与热更新调试。

它解决的问题:模拟器与真实浏览器行为不一致导致的"本地通过、生产出问题",尤其是布局相关、浏览器专有 API(如 scroll 行为、requestIdleCallback 细节)与真实事件派发场景;同时组件测试可在真实浏览器中做视觉级验证。取舍:速度慢于 jsdom、CI 需要浏览器环境(容器内安装),适合对浏览器真实性要求高的用例;工程上可按用例分层,简单逻辑用模拟器、浏览器相关用 Browser Mode。

考察 Vitest 的现代化方向:把"真实浏览器"作为一等测试环境,与模拟器形成分层选择;回答应说明原理、解决的问题与资源配置代价。

#
★★★

21. 断言风格(expect、assert)与测试组织

expect 与 assert 两种断言风格有何差异?测试如何组织才清晰可维护?

  • expect 链式匹配器与 assert 函数式断言
  • describe/it 层级与钩子函数
  • AAA(Arrange-Act-Assert)组织模式

expect(Jest/Vitest 风格)采用链式匹配器表达行为期望,如 expect(result).toBe(2)、expect(fn).toHaveBeenCalledWith(x),语义贴近自然语言、失败信息丰富;assert(node:assert 风格)是函数式断言,如 assert.strictEqual(a, b)、assert.throws(fn),零依赖、与 Node 原生绑定,适合轻量场景。两者可混用,但团队应统一风格以降低认知成本。

测试组织上:describe 分组描述被测单元的行为维度,it/test 描述具体行为,beforeEach/afterEach 管理准备与清理;推荐 AAA 模式——Arrange(准备数据与依赖)、Act(执行被测动作)、Assert(断言结果),并在用例命名上用"should 行为 when 条件"的句式表达意图。良好的组织让失败信息直接对应业务行为,缩短定位链路。

考察断言风格差异(链式 expect vs 函数式 assert)与测试组织规范(describe 层级、钩子、AAA),回答体现"测试即文档"的维护性思想。

#
★★★

22. Vitest 的 vi.mock/vi.spyOn 与 Vitest/Jest 30 的现代取舍

Vitest 的 vi.mock/vi.spyOn 与 Vitest/Jest 30 在现代工程中有哪些取舍?

  • vi.mock/vi.spyOn 的语义差异与使用边界
  • Jest 30 的现代演进方向
  • 两大生态的迁移与共存策略

vi.mock 是模块级替换(自动 hoist),用于消除依赖副作用;vi.spyOn 针对对象方法记录调用并可替换实现,粒度更细。使用原则:能用 spyOn 做行为验证时不滥用 mock;只有模块边界(网络、时间、第三方 SDK)才整体 mock;注意 vi.hoisted 声明工厂中引用的变量,避免 TDZ 错误。Jest 30 的演进方向是更现代的 ESM 支持、更快的运行器(向 Rust 迁移的 jest-rust 实验)与更清晰的 mock 语义,但兼容层历史包袱仍在。

取舍要点:Vitest 在速度、原生 ESM 与 Vite 集成上占优,且 mock API 与 Jest 高度兼容(vi 与 jest 命名空间几乎一一对应),迁移成本低;Jest 30 适合存量项目渐进升级,借助 SWC/Rust 加速。团队策略:新项目选 Vitest,存量项目评估迁移收益;无论哪个,mock 语义(hoisting、模块图、并发隔离)差异需在迁移清单中逐项验证。

考察 mock API 的精确语义与两大运行器的现代演进:vi.mock 管模块、vi.spyOn 管行为,Jest 30 与 Vitest 在速度与 ESM 上的竞争推动生态趋同。

#
★★★

23. Ladle 在 E2E 与单元测试的协作

Ladle 如何与 E2E 与单元测试协作?其轻量设计带来哪些工程价值?

  • Ladle 作为轻量 Storybook 替代的能力
  • 作为组件测试与 E2E 的稳定渲染入口
  • 启动速度与 CI 资源收益

Ladle 是基于 Vite 的轻量组件开发与测试服务器,核心能力与 Storybook 对齐:CSF 格式的 story、args、play 函数与 addons 体系,但去掉了重型依赖,启动与构建速度显著更快(秒级冷启动)。它可以作为组件测试的统一渲染入口:Vitest 测试或 E2E 工具直接访问 story 路由挂载组件,实现"一套 story 描述,多处复用"。

与 E2E 的协作:Playwright/Cypress 可导航到 Ladle 的 story URL 进行组件级 E2E 验证,获得稳定的组件环境与视觉检查能力;与单元测试的协作:Ladle 的 story 数据可驱动 Vitest 生成用例(类似 Storybook vitest plugin 的思路)。工程价值:CI 中轻量启动降低资源占用、缩短反馈周期;团队无需引入重型工具即可获得组件开发、文档与测试一体化体验。

考察轻量组件开发工具在现代测试金字塔中的位置:Ladle 以速度换取生态兼容性,作为 story 渲染与测试入口与 E2E/单测协作。

#
★★★

24. Happy DOM 与 jsdom 在 Vitest 单测 DOM 环境的工程取舍

在 Vitest 单测 DOM 环境中,Happy DOM 与 jsdom 应如何取舍?

  • environment 配置与全局 API 注入
  • happy-dom 的速度与 API 完整度
  • 与项目依赖兼容性的验证

Vitest 通过 environment: 'jsdom' 或 'happy-dom' 配置 DOM 模拟环境,两者都为测试注入 window/document 等全局。jsdom 历史最久、生态兼容面最广,第三方库对其特定行为(如 layout 依赖、requestAnimationFrame polyfill)适配充分;happy-dom 执行更快、对现代 API(shadow DOM、Web Components、表单控件)支持更完整,但个别行为与 jsdom 不一致。

工程取舍:项目依赖较重、依赖 jsdom 特有行为时选 jsdom 求稳;追求测试速度、组件以现代 Web API 为主时选 happy-dom;也可以在 CI 与本地分层验证。无论选哪个,都应验证关键依赖(如事件库、拖拽库)在目标环境下的行为,必要时用 environmentOptions 或 per-file 注释(// @vitest-environment happy-dom)做混合配置,兼顾速度与兼容。

考察 Vitest DOM 环境配置的实战取舍:速度 vs 兼容性,回答应给出"按依赖验证结果选择 + 混合环境"的工程策略。

#
★★★

25. 发测试(test.concurrent)与测试隔离(isolate: true)的应用

test.concurrent 与测试隔离(isolate: true)各自解决什么问题?如何配合使用?

  • test.concurrent 的文件内并发执行
  • isolate 的进程/线程级隔离机制
  • 并发下的共享状态与资源冲突治理

test.concurrent 让同一文件内的多个用例并发执行,适用于彼此独立的用例(如不同输入的不同分支),可缩短单文件执行时间;但并发用例共享同一模块实例与全局状态,任何共享变量、定时器或 mock 状态都可能串扰,因此只应标记确实无共享状态的用例。isolate: true(Vitest 默认)为每个测试文件建立独立的环境与模块缓存(通过 V8 隔离或 worker 进程),防止文件间的全局污染。

配合使用:文件间隔离靠 isolate,文件内并发靠 test.concurrent,两者叠加时注意数据库、文件系统、端口等外部资源的隔离——并发用例应使用独立资源或事务,避免互踩;CI 中还要与 workers 并行(多进程)区分开:workers 是文件级并行,concurrent 是文件内并行。排查并发问题时,可临时 isolate: false 复现共享状态冲突,验证是否由隔离缺失引起。

考察并发与隔离两个维度的测试执行模型:isolate 管文件间环境隔离、concurrent 管文件内并发,回答应强调共享状态风险与资源隔离。

#
★★

26. Property-based Testing(fast-check)

基于属性的测试(Property-based Testing,如 fast-check)的原理是什么?适用于哪些场景?

  • 属性(不变量)与 arbitraries 随机生成
  • 反例最小化(shrinking)机制
  • 与例程化测试的互补

基于属性的测试不写"具体输入→期望输出"的用例,而是声明被测代码应满足的不变量(property),由框架用 arbitraries 随机生成大量输入去验证。例如"任意数组排序后长度不变、元素集合不变";fast-check 提供 fc.property、fc.integer/fc.array 等生成器与 fc.assert。当断言失败时,shrinking 机制自动把反例逐步最小化(如把超长字符串缩短为最小触发输入),大幅降低定位成本。

适用场景:解析与序列化(round-trip:任意值序列化后再反序列化应还原)、排序与去重算法、校验逻辑、状态机转换;对边界值(0、负数、空、超大数、Unicode)的覆盖远超手写用例。局限:不变量声明需要设计成本,纯业务 UI 流程难以抽象属性,应把它作为例程化测试的补充而非替代,两者共用同一断言体系。

考察属性测试的核心机制:不变量 + 随机生成 + 反例最小化,回答应明确其适用边界(算法/解析类)与补充定位。

#
★★

27. TDD/BDD 的流程差异

TDD 与 BDD 的流程有何差异?实践中如何结合?

  • TDD 红-绿-重构循环
  • BDD 的 Given-When-Then 行为语言
  • 两者的适用层次与结合方式

TDD(测试驱动开发)是开发方法论:先写失败测试(红),再写最小实现使其通过(绿),然后重构(Refactor),循环往复;它强制"先想清楚行为再实现",通过测试提供安全网驱动设计。BDD(行为驱动开发)是协作与表达方法:用 Given-When-Then 自然语言描述行为场景,强调业务方、开发与测试用同一语言沟通,工具如 Cucumber 把特性描述映射到自动化步骤。

差异与结合:TDD 关注"如何开发"(节奏),BDD 关注"如何表达与验收"(语义);BDD 适合需求级(用户故事、验收标准),TDD 适合实现级(函数、组件逻辑)。实践中常用"BDD 描述外部行为 + TDD 驱动内部实现":需求用 Given-When-Then 定义验收,内部用 TDD 红绿循环驱动,E2E 层承载 BDD 场景、单元层承载 TDD 用例,两者互补形成完整质量链。

考察两种开发方法论的本质差异:TDD 是测试先行的开发节奏,BDD 是行为表达与协作语言,回答应说明适用层次与结合策略。

#
★★

28. 覆盖率(Istanbul/nyc/v8)的行/分支覆盖

行覆盖与分支覆盖有何区别?Istanbul/nyc 与 v8 覆盖率实现有何差异?

  • 行覆盖与分支覆盖的统计口径
  • Istanbul 插桩与 V8 原生覆盖的实现差异
  • 分支覆盖对漏测路径的暴露价值

行覆盖(lines)统计"执行过的代码行占总行数比例",只反映语句是否被执行;分支覆盖(branches)统计 if/三元/switch/逻辑运算等分支的每个去向是否被走到,能暴露"某个 else 分支从未被测试"的漏测路径。例如仅覆盖 if 为 true 的路径,行覆盖可能是 100%,而分支覆盖只有 50%——分支覆盖更能代表测试的路径完备性。

实现差异:Istanbul/nyc 通过源码插桩(在语句与分支处注入计数器)统计,兼容性好但转译有性能开销;V8 原生覆盖率(v8 provider)由 V8 引擎直接报告执行过的代码区域,无插桩开销、更准更快,但只支持 V8 运行时。实践中应同时关注行与分支维度,CI 门禁至少同时设置 lines 与 branches 阈值,避免"高行覆盖、低分支覆盖"的假安全感。

考察覆盖率维度的精确语义:行覆盖衡量语句执行、分支覆盖衡量路径完备,回答应强调分支覆盖对漏测的暴露价值与两种实现的技术差异。

#
★★

29. Playwright 的 trace viewer 与 Mock/Stub/Spy 的协作

Playwright 的 trace viewer 如何与 Mock、Stub、Spy 协作定位问题?

  • page.route 的请求拦截与 stub
  • 对拦截行为做 spy 式断言
  • trace 中观察 mock 前后的请求差异

Playwright 通过 page.route(pattern, handler) 在浏览器网络层拦截请求:handler 中可 fulfill(返回固定响应,即 stub)、abort(模拟失败)或 continue(放行);结合请求次数、URL 与载荷的记录可实现 spy 式验证,例如 expect(requestCount).toBe(2) 或断言请求参数。这使 E2E 既能走真实交互,又能在网络边界注入确定性。

与 trace 的协作:trace 会记录被 route 拦截的请求及其处理结果,调试时可对比"mock 响应"与"真实响应"的差异,确认是测试桩问题还是页面逻辑问题;还能看到 handler 中 fulfill 的响应体,判断 stub 数据是否符合页面假设。实践建议:route handler 中打日志或命名 route(page.route(..., { times: 1 }))控制次数,配合 trace 时间轴验证拦截时机,快速区分"页面 bug"与"mock 错误"。

考察 Playwright 网络层测试替身的完整闭环:route 拦截(mock/stub)+ 断言(spy)+ trace 回放(定位),回答体现三者协作的排障思路。

#
★★

30. Vitest 与 Mock/Stub/Spy 的协作

Vitest 中 Mock、Stub、Spy 如何协作构建测试?

  • vi.fn/vi.mock/vi.spyOn 的分工
  • 断言匹配器 toHaveBeenCalledWith 等
  • 清理与恢复的最佳实践

Vitest 提供三层测试替身:vi.fn() 创建可断言的 stub 函数(可传实现或返回固定值);vi.mock 替换整个模块,配合工厂函数返回 stub 集;vi.spyOn 包装对象方法,保留原实现或替换实现并记录调用。协作模式:对模块依赖用 vi.mock 消除副作用,对内部协作对象用 vi.spyOn 记录交互,对纯回调用 vi.fn 注入并断言,三者按"影响面从大到小"选择。

断言层面,expect(spy).toHaveBeenCalledWith(...)、toHaveBeenCalledTimes(n)、mock.calls 数组可精确验证调用行为;清理上 afterEach 中 vi.restoreAllMocks()(恢复被 spyOn 改写的实现)、vi.clearAllMocks()(清空调用记录)、vi.resetAllMocks()(重置实现)各司其职,防止用例间污染。工程上建议把"依赖注入 + 边界 mock + 行为断言"固化为团队规范,避免随意 mock 全模块导致测试失真。

考察 Vitest 测试替身的完整工具箱:fn/mock/spyOn 的分工与断言、清理匹配,回答应强调"按影响面选择替身"与清理规范。

#
★★

31. 单元测试在 CI 与生产联调中应建立哪些可观测性、日志与排障手段,才能让一次失败快速定位到 commit 与用例

单元测试在 CI 与生产联调中应建立哪些可观测性、日志与排障手段,才能让一次失败快速定位到 commit 与用例?

  • 测试报告与 CI artifact 的关联(JUnit/HTML 报告)
  • 日志规范:用例标识、分组与分级
  • 与 commit、构建号的关联追踪

单元测试的可观测性建设包含三层:一是结构化报告,CI 中生成 JUnit XML(供平台解析)与 HTML 报告(含失败堆栈、耗时、环境信息),作为 artifact 归档;二是日志规范,测试内使用带用例标识(如 describe+it 全名)的分级日志,失败时输出上下文(输入数据、mock 调用记录、环境变量),并限制敏感信息;三是溯源关联,把测试运行号与 git commit SHA、PR 号、构建号绑定,失败通知中直接附链接。

排障手段上:失败用例自动重试策略与 flaky 标记分离(区分偶发与环境问题);失败时自动保存 trace/screenshot/覆盖率快照;CI 中按 commit 增量只跑受影响用例(测试影响分析),缩短定位范围;生产联调阶段,测试失败信息与 RUM/APM 的 requestId 关联,可追溯同一用户会话。目标:任何一次失败,开发者可从通知在 2 分钟内定位到"哪个 commit 引入了哪个用例的回归"。

考察测试运维化的工程能力:报告、日志、溯源三位一体,回答应强调"失败可追溯、范围可收窄、信息可复现"三大目标。

#
★★

32. MSW(Mock Service Worker)在网络层 Mock 的应用

MSW(Mock Service Worker)在网络层 Mock 的价值是什么?如何组织 handler?

  • 网络层 mock 与函数级 mock 的本质区别
  • handler 的声明式组织与复用
  • 浏览器/Node 双端一致性

MSW 把 mock 放在网络层:浏览器端由 Service Worker 拦截请求,Node 端由拦截器接管,业务代码无需任何改动即被 mock,与函数级 mock(vi.mock 替换模块)相比,它验证的是"真实请求路径 + mock 响应",更贴近生产行为,且一套 handler 描述在浏览器与 Node 测试中完全复用。

组织实践:按领域建立 handler 文件(如 user.ts、order.ts),用 http.get('/api/users') 声明式描述接口;场景化组织(基础 handler + 按测试 override)支持局部定制;配合 MSW 的 byPattern 匹配与延迟模拟(delay)验证加载态。边界:MSW 无法覆盖 Service Worker 之外的传输(WebSocket 可用 ws 拦截)、无法模拟真实网络条件(带宽/延迟分布),这些仍需 Puppeteer/Playwright 的网络模拟补充。

考察网络层 mock 的层次思想:不侵入业务代码、双端复用、贴近真实请求路径,回答应包含 handler 组织与能力边界。

#
★★

33. WebDriverIO 与 Selenium 的现代取舍

WebDriverIO 与 Selenium 在现代工程中有哪些取舍?

  • Selenium 的 WebDriver 标准与多语言生态
  • WebdriverIO 的现代封装与测试能力
  • 与 Playwright/Cypress 的竞争关系

Selenium 是 WebDriver 自动化标准的实现者:协议跨语言(Java/Python/JS)、支持全部主流浏览器与远程网格(Selenium Grid),适合既有重型自动化体系;代价是 API 底层、同步模型需要显式等待、维护成本高。WebdriverIO 构建在 WebDriver 协议之上提供现代封装:原生支持 ESM/TS、自动等待(waitFor)、链式 API、测试运行器(WDIO Testrunner)与丰富服务(devtools、appium、visual regression、allure 报告)。

现代取舍:新项目往往在 Playwright(多浏览器、快速、内置断言与 trace)与 Cypress 之间选择,WebdriverIO 的差异化在于跨浏览器网格兼容(可接 Selenium Grid)与移动端(Appium)能力,适合需要"Web + 移动 + 远程设备矩阵"的团队;纯 Web 新项目则优先 Playwright。无论选哪个,都应评估维护成本、团队技能与 CI 基础设施的适配。

考察自动化框架的演进谱系:Selenium 是协议标准、WebdriverIO 是其现代封装,回答应结合移动端与网格需求给出选型建议。

#
★★

34. Playwright 1.5x 在大型项目测试金字塔的工程价值

Playwright 1.5x 在大型项目的测试金字塔中承担什么角色?其工程价值是什么?

  • 测试金字塔分层与 Playwright 的定位
  • 组件测试、E2E 与性能测试一体化
  • 大型项目的并行、复用与维护治理

测试金字塔主张"底层单元测试多而快、上层 E2E 少而稳",Playwright 1.5x 通过三方面支撑大型项目:一是组件测试(mount 组件到真实浏览器),补齐金字塔中"组件层"的自动化覆盖;二是 E2E 的跨浏览器矩阵与 worker 并行,把慢速 E2E 的 CI 时间压到可接受范围;三是 test.use 的 fixtures 与项目级配置,支持多环境(多浏览器、多设备、多权限)复用一个测试集。

工程价值:fixtures 机制(自定义 test fixture)实现登录态、数据准备的声明式复用,降低用例耦合;Web-First 断言与自动等待减少 flaky;trace 与报告体系支撑大规模维护。典型分层实践:Vitest 单测覆盖逻辑、Playwright 组件测试覆盖组件交互、Playwright E2E 覆盖关键用户旅程,每层数量按金字塔比例分配,预算内保持 CI 稳定。

考察测试金字塔在现代框架下的落地:Playwright 1.5x 以组件测试填补中间层,以并行与 fixtures 支撑大规模维护,回答应体现分层数量与职责分配。

#
★★

35. page.evaluate 与边界(main world vs isolated world)

page.evaluate 在 main world 与 isolated world 之间有何边界?

  • evaluate 的执行上下文与参数序列化
  • main world 与 isolated world 的隔离语义
  • 跨世界访问页面变量与 DOM 的方式

page.evaluate(fn, arg) 在浏览器页面上下文中执行函数:参数与返回值通过结构化克隆传递(不支持函数、类实例等不可克隆对象),函数本身在浏览器中序列化执行。Playwright 的脚本默认运行在 isolated world:该世界与页面共享 DOM 但不共享 JavaScript 全局变量,因此可以查询与操作 DOM(如 document.querySelector),却无法读取页面里的 window.myGlobal 或页面定义的全局函数。

边界与跨世界访问:需要读页面 JS 状态时,可在 evaluate 内部通过页面世界代码执行(如在 fn 内 new Function 或注入 script),或使用 page.addScriptTag 在页面主世界注入;反之页面主世界也无法访问 isolated world 的变量。工程影响:测试中"页面变量是否可见"取决于执行世界,混合框架(如 Vue/React 全局挂载)下的断言需显式处理;这也是 page.evaluate 与 page.locator 断言(自动等待)互补的原因——evaluate 适合取数据,locator 适合语义断言。

考察浏览器自动化中的执行世界模型:isolated world 共享 DOM 不共享 JS 全局,回答应说明跨世界访问的限制与解法。

#
★★

36. Playwright Trace Viewer 与 Codegen 在测试维护的工程价值

Playwright 的 Trace Viewer 与 Codegen 在测试维护中有哪些工程价值?

  • Codegen 录制生成选择器与断言的效率
  • Trace Viewer 的失败回放与协作调试
  • 维护成本控制与最佳实践

Codegen(npx playwright codegen)通过浏览器录制用户操作,自动生成 locator 与断言代码,作为测试编写的起点可大幅提速,尤其是探索不熟悉的页面结构与选择器时;生成的代码遵循用户视角查询与自动等待规范,比手写选择器更稳健。Trace Viewer 则服务于"写完之后":任何失败都能回放完整操作序列、检查每一步 DOM 快照与网络,即使 CI 环境也能本地分析。

两者结合的维护价值:Codegen 降低编写门槛、保证选择器质量,Trace Viewer 降低失败定位成本,共同缓解"E2E 易写难养"的核心痛点。最佳实践:Codegen 产出后需人工审视(合并重复 locator、提取 page object/fixtures)、用 trace on-first-retry 控制录制开销、定期清理无用用例;把两者纳入团队工作流(PR 必附 trace、失败自动归档)可显著提升大型项目的 E2E 可持续性。

考察 Playwright 开发者体验工具链:Codegen 解决"怎么写",Trace Viewer 解决"怎么调",回答应强调两者对长期维护成本的系统性降低。

#
★★

37. Storybook + Chromatic 的视觉回归测试

Storybook 与 Chromatic 如何实现视觉回归测试?流程中的关键环节有哪些?

  • 快照比对与像素级 diff 原理
  • 基线管理与审批工作流
  • TurboSnap 与跨浏览器快照

Storybook 将每个组件以 story 形式独立渲染,Chromatic 抓取每个 story 在指定浏览器视口下的截图,与基线(baseline)截图做像素级 diff,差异以热力图形式呈现;开发者对差异做出"接受(更新基线)"或"拒绝(修复回归)"的决策,形成视觉回归的审批工作流。跨浏览器快照可捕获浏览器间渲染差异,多视口覆盖响应式问题。

关键环节:基线管理(合并后自动更新,避免 PR 内 diff 污染)、TurboSnap(按依赖图只测试受影响的 story,节省 CI 时间)、与 GitHub 集成的 PR 状态检查(差异必须人工确认才能合入)。工程价值:视觉回归前移到 PR 阶段,与代码评审并行,防止"样式被悄悄改坏";对设计系统与组件库项目收益最大,配合 a11y 快照还能捕获对比度类可访问性回归。

考察视觉回归的完整闭环:快照抓取、像素 diff、基线审批,回答应突出基线管理与人工确认环节的工程意义。

#
★★

38. Storybook 的 play 函数与交互测试 在团队规范与可维护性的工程价值

Storybook 的 play 函数与交互测试对团队规范与可维护性有哪些工程价值?

  • play 函数在 story 内模拟交互与断言
  • 单一描述源:文档、视觉、交互共用 story
  • 团队协作与维护成本降低

play 函数在 story 渲染后执行:使用 testing-library 风格的 API(canvas.findByRole、userEvent.click)模拟用户交互并对结果断言(如 expect(element).toHaveText),使 story 从"静态展示"升级为"可执行的交互规格"。由于 story 同时驱动文档(Docs)、视觉回归(Chromatic)与交互测试(Test Runner/Vitest plugin),形成"单一描述源":组件行为只维护一份描述,变更一处即同步所有环节。

团队价值:story 的可读性让产品、设计、开发共用同一种"组件行为语言",交互测试即行为契约;play 断言失败直接指出"哪个交互期望被破坏",回归定位到组件级;配合 CI 并行,组件质量门槛前置。规范建议:为关键交互必配 play、保持 story 粒度与组件 API 对应、禁止在 play 中写与渲染无关的业务逻辑,维护成本随组件库规模线性可控。

考察 play 函数对组件测试体系的提升:story 成为可执行规格与单一描述源,回答应强调团队协作与维护性的结构化收益。

#
★★

39. MSW 与 Playwright 的 API 拦截协作

MSW 与 Playwright 如何协作进行 API 拦截?各自的边界在哪里?

  • Playwright page.route 与 MSW 的机制差异
  • 测试中共享 handler 描述的方式
  • 协作边界与典型分工

Playwright 通过 page.route 在浏览器网络层拦截,适合测试内局部的、过程性的 stub(按 URL/请求方式匹配、动态 fulfill);MSW 在浏览器端用 Service Worker 拦截,handler 以模块化声明组织,适合跨测试复用的接口描述。两者机制不同(route 是协议层注入,MSW 是 worker 层),但可协作:把 MSW 的 handler 描述作为"接口契约源",Playwright 测试中需要特殊场景时用 route 局部覆盖。

协作方式:在 Playwright fixture 中启动 MSW(worker.start)并让测试依赖其默认 handler,同时用 page.route 处理测试内临时场景(如注入异常响应、模拟超时);注意 MSW worker 与 Playwright 的浏览器上下文共存时,需在 page 创建后正确初始化。边界:MSW 更擅长"接口语义 mock"(统一描述、双端复用),route 更擅长"本次测试的临时控制";两者共用时保持 handler 单一来源,避免同一接口两处定义导致行为漂移。

考察两大拦截机制的协作:MSW 管接口语义复用、route 管测试内临时控制,回答应明确"单一 handler 源 + 局部覆盖"的分工。

#
★★

40. Cypress 14+ 在端到端测试的真实浏览器与开发体验的现代应用

Cypress 14+ 在端到端测试中如何体现真实浏览器执行与现代开发体验?

  • Cypress 14 的架构与真实浏览器执行
  • 交互式 Runner、时间旅行与命令日志
  • 与现代前端工程(组件测试、网络 stub)的集成

Cypress 14+ 延续"测试代码与页面同进程"的架构,在真实浏览器(Chromium 系,Cypress Cloud 支持 Firefox/WebKit 实验)中执行,事件真实派发、CSS 真实生效;其交互式 Runner 提供可视化命令执行列表、每个命令前后的 DOM 快照(时间旅行)、选择器调试器与失败重试,开发体验直观——写测试时可实时观察每一步的效果。

现代应用:cy.intercept 拦截与修改网络响应(含延迟、错误注入)实现确定性;cy.session 复用登录状态减少重复;组件测试(mount)与 E2E 共享同一 Runner;配合 Cypress Cloud 的并行(test splitting)与录制(replay)在 CI 中规模化。取舍:同进程代理带来调试便利,也带来跨域与多标签的限制;现代项目常按"组件用 Cypress 组件测试、关键旅程用 Cypress E2E"的组织方式落地。

考察 Cypress 的真实浏览器执行与开发者体验设计:时间旅行、命令日志、可视调试是其差异化优势,回答应落到现代工程集成。

#
★★

41. Playwright 在跨浏览器(Chromium、WebKit、Firefox)

Playwright 的跨浏览器能力(Chromium、WebKit、Firefox)如何应用?有哪些工程要点?

  • 三浏览器驱动的协议机制(CDP/BiDi)
  • 浏览器矩阵的 CI 编排与并行
  • 跨浏览器差异测试的策略

Playwright 通过 CDP(Chromium)与 WebDriver BiDi/内置协议驱动 Chromium、WebKit、Firefox 三种引擎,同一套测试代码在 projects 配置中声明浏览器矩阵即可运行:projects 支持按浏览器/设备/权限划分,CI 中并行执行。WebKit 的加入能提前暴露 Safari 系兼容问题(部分 CSS 特性、PWA、存储行为),Firefox 覆盖 Mozilla 系用户,形成主流引擎全覆盖。

工程要点:并行执行按浏览器数线性分配 worker,CI 需配置足够并发与分片(sharding);跨浏览器失败要先区分"引擎差异"与"测试问题",用 trace 对比;对仅 Chromium 支持的特性(如某些 API)用 test.skip(condition) 按浏览器条件跳过;建议以 Chromium 为主要开发浏览器、其余浏览器做回归矩阵,并用 WebKit 快照工具(webkitWebView)处理平台差异,平衡成本与覆盖。

考察跨浏览器测试的落地:projects 矩阵、并行与分片、按引擎条件的跳过策略,回答应体现"主浏览器开发 + 矩阵回归"的成本平衡。

#
★★

42. Vitest 1.x 在 Vite 项目中的测试速度与现代实践

Vitest 1.x 在 Vite 项目中如何实现测试速度优势?有哪些现代实践?

  • 复用 Vite 配置与模块图的提速原理
  • watch 模式与 HMR 开发体验
  • 并发、缓存与 CI 加速配置

Vitest 1.x 直接复用项目的 Vite 配置(plugins、resolve、alias),无需像 Jest 那样维护独立的 transform 配置;基于 Vite 的模块图,测试转换按需进行(只转换被导入的模块),esbuild 转译快、内置缓存避免重复编译,因此冷启动与热重载远快于传统转译链路。watch 模式下测试文件改动即时重跑、依赖模块改动按需触发,开发反馈接近毫秒级。

现代实践:pool: 'threads'/'forks' 与 maxWorkers 调优并发;coverage 用 v8 provider 减少开销;CI 中缓存 node_modules 与 Vite 缓存(.vite)、用 --reporter=json 输出结构化结果;对纯函数模块用 browser mode 或 happy-dom 分层;大型仓库用 workspace 与 test.projects 分离单元/集成测试,隔离慢用例。核心是"复用 Vite 生态 + 按需转换 + 并行池"三条提速路径。

考察 Vitest 的速度原理(Vite 模块图、esbuild、缓存)与工程化提速实践,回答应包含开发与 CI 双场景的配置要点。

#
★★

43. Component Story Format 3(CSF3)

Component Story Format 3(CSF3)解决了什么问题?其结构有何特点?

  • CSF 的导出式 story 声明
  • CSF3 对 args/play 函数的简化
  • 与测试、文档、视觉回归的联动

CSF(Component Story Format)是 Storybook 的 story 声明标准:一个模块导出一个 default export(meta,含 component、title、args、decorators)与多个 named export(每个即一个 story,含自身 args 与 play)。CSF3 简化了书写:story 可直接是组件参数对象({ args: {...} })而非函数,支持 play 函数内联交互、render 自定义渲染与 tags 元数据,让 story 更接近"组件配置"而非"渲染脚本"。

价值:story 是跨工具的标准资产——同一描述驱动文档(autodocs)、视觉回归(Chromatic)、交互测试(play)与单元测试(Vitest plugin),消除多套描述;args 机制天然支持属性矩阵(同一个 story 不同参数组合),配合 play 实现"每个交互状态一个可执行规格"。CSF3 是组件测试现代化的基础设施:结构简洁、可读性强、易被工具链消费。

考察 CSF 标准的演进:导出式声明、args 矩阵与 play 函数使其成为跨工具可执行规格,回答应说明 CSF3 的简化点与生态联动。

#
★★

44. Playwright 的 page.locator 与自动等待(auto-waiting)的工程价值

Playwright 的 page.locator 与自动等待(auto-waiting)如何降低测试脆弱性?

  • locator 的语义化查询与链式过滤
  • 自动等待的作用点(可见、可操作、稳定)
  • 与显式等待、超时配置的协作

page.locator() 返回惰性定位器:创建时不立即查询,执行操作/断言时才解析,且每次操作前自动等待元素满足前置条件——存在、可见、稳定(不连续变化)、可接收事件(未被遮挡)——再执行点击、输入等动作,超时后给出带超时点信息的失败。这消除了手写 sleep 与轮询,把"等多久"交给框架的智能重试,是 Web-First 原则的核心。

工程价值:自动等待显著降低时序类 flaky(元素晚加载、动画中、请求未完成);locator 的严格模式(命中多个时报错)与 filter/hasText 链式组合保证定位唯一性;配合 test.setTimeout 与 expect 超时配置,可对慢环境(CI)统一放宽。最佳实践:优先 getByRole/getByText 语义定位、避免在 CSS 选择器中耦合实现细节、用 expect(locator).toBeAttached() 等断言表达意图而非裸等待。

考察 Playwright 自动等待的机制与价值:操作前条件校验 + 智能重试替代手写等待,回答应说明其与语义化定位共同构成防 flaky 体系。

#
★★

45. Visual Regression Testing(Percy、Chromatic)

Visual Regression Testing(Percy、Chromatic)的原理与工程实践是什么?

  • 截图比对与像素 diff 原理
  • 基线与审批工作流
  • 与 CI 的集成及快照维护

视觉回归测试(VRT)对页面/组件截图并与基线比对,检测非预期视觉变化。Percy 与 Chromatic 的典型流程:CI 中快照 DOM 上传云端渲染,与基线做像素级 diff,差异以热力图展示;维护者审批(接受为新基线或标记回归),历史基线版本可回溯。Chromatic 面向 Storybook(组件级、TurboSnap 按依赖增量),Percy 面向页面级与组件级(SDK 支持多框架、DOM snapshot 上传避免本地渲染差异)。

工程实践:快照应在稳定环境执行(禁动画、固定字体/视口、stub 网络与时间)保证可复现;基线更新走审批流而非自动合并,防止视觉漂移被静默接受;结合 CI 状态检查(差异未审批不可合入)与 Slack 通知。维护要点:控制快照数量(只覆盖关键状态)、用 approve 阈值忽略细微环境差异(亚像素)、定期清理过时快照,让 VRT 成为"变更提示器"而非"噪声源"。

考察 VRT 的完整工程闭环:快照、diff、审批、基线治理,回答应强调稳定环境与审批流程对可维护性的关键作用。

#
★★

46. MSW(Mock Service Worker)在浏览器与 Node 测试的 API mock 协作

MSW 如何在浏览器与 Node 测试间实现 API mock 的协作?

  • 双端拦截机制(Service Worker 与 interceptors)
  • 同一 handler 描述的双端复用
  • 跨测试类型的一致性收益

MSW 的核心价值是"一次描述、双端运行":浏览器端通过注册的 Service Worker 拦截 fetch/XHR,Node 端通过 @mswjs/interceptors 拦截 http/https 模块请求,两端的 API(setupWorker/setupServer)共用同一套 handler 定义。因此单元/集成测试(Node)与 E2E(浏览器)对接口的 mock 语义完全一致,测试替身不再因环境分裂为两套。

协作实践:把 handler 按领域组织为可复用模块(如 handlers/user.ts),setupServer 与 setupWorker 都从同一源导入;Node 端用 server.listen()/resetHandlers()/close() 管理生命周期;浏览器端注意 worker.start() 的时序与更新。收益:接口变更时只改一处描述,单测与 E2E 同时得到真实反馈;mock 与契约(Pact/OpenAPI)数据对齐,接口演进时回归面可控。

考察 MSW 双端一致性的工程价值:同一 handler 源驱动 Node 与浏览器测试,消除环境分裂的 mock 维护成本。

#
★★

47. Testing Library 的 getByRole、getByLabelText 在无障碍测试的工程实践

Testing Library 的 getByRole、getByLabelText 在无障碍测试中有哪些工程实践?

  • getByRole 的 ARIA 角色查询与 name 匹配
  • getByLabelText 对 label 关联的验证
  • 查询失败暴露的无障碍问题

getByRole(role, { name }) 按 ARIA 角色(button、dialog、tab 等)与可访问名称(accessible name,由文本/label/aria-label 计算)查询元素,是 Testing Library 推荐的首选查询:角色与名称正是辅助技术用户感知元素的方式,查询成功即隐式验证了元素的语义与命名正确。getByLabelText 按 label 文本关联表单控件,查询失败通常意味着 label 未正确关联(无 for/id、包裹结构错误或 aria-label 缺失)。

工程实践:把"查询路径"视为可访问性检查——统一用 getByRole 查询交互元素(按钮、对话框、tab),用 getByLabelText 验证表单可读性,用 getByText 验证内容呈现;配合 jest-axe 做规则级检查,形成"查询语义 + 规则校验"双保险。对查询失败的修复(补 role、补 label、修 name)本身就是无障碍改进,测试用例因此成为无障碍需求的活文档。

考察 Testing Library 语义查询与可访问性的内在绑定:查询方式即无障碍验证,回答应体现"以查询路径驱动无障碍修复"的实践。

#
★★

48. Cypress Component Testing 在组件隔离测试相较 E2E 的取舍

Cypress Component Testing 在组件隔离测试方面相较 E2E 有哪些取舍?

  • 组件级隔离与 E2E 全链路覆盖的差异
  • 速度、稳定性与调试体验对比
  • 测试金字塔中的分工定位

Cypress Component Testing 在真实浏览器中挂载单个组件(真实 CSS、真实事件),与 E2E 的区别在于测试范围:组件测试隔离了路由、后端与兄弟组件,直接以组件 props 与交互为输入,问题定位精确到组件内部;E2E 覆盖完整用户旅程(路由、鉴权、数据流),验证系统集成,但任何一环不稳定都会导致失败,且失败定位成本高。

取舍对比:组件测试更快(无需完整应用启动与登录流程)、更稳定(依赖面小)、调试更聚焦,但无法发现跨组件与后端集成问题;E2E 最接近真实用户但慢、脆、维护贵。实践定位:金字塔中间层——高频交互逻辑用组件测试(每个组件 3-8 个用例),关键用户旅程用 E2E(每旅程 1-2 条),两者共享 Cypress 语法与 Runner 体验,团队心智成本低。

考察组件测试与 E2E 的本质分工:隔离粒度 vs 集成广度,回答应落到金字塔分层与数量配比,体现成本-覆盖权衡。

#
★★

49. flaky(不稳定)测试的治理,重试策略、确定性因素(时间/网络/顺序)、flaky 报告与 CI 中的隔离与修复流程?

flaky(不稳定)测试如何治理?重试策略、确定性因素、flaky 报告与 CI 中的隔离修复流程分别是什么?

  • 重试策略与 flaky 标记的边界
  • 不确定性来源:时间、网络、执行顺序
  • flaky 报告、隔离与修复闭环

flaky 测试指同一代码下时好时坏,常见根因:时间相关(真实等待、定时器竞争)、网络相关(真实请求、限速)、顺序相关(用例间共享状态、全局 mock 未清理)、环境相关(资源竞争、浏览器版本)。治理第一步是区分"偶发失败"与"真实回归":CI 配置失败重试(Playwright retries、Jest retryTimes)并标记 flaky,但重试只是止血——重试掩盖问题,需建立 flaky 报告机制(CI 平台统计重试后成功的用例、记录失败率),定期清理。

确定性改造:时间用 fake timers、网络用 MSW/route 拦截、用例间隔离(独立状态/数据库事务/清理钩子)、顺序无关(describe 内独立、并行资源隔离)。CI 闭环:失败自动采集 trace/日志归档 → flaky 标记与告警 → 按根因分类修复(超时放宽、隔离共享资源、删除无意义断言)→ 修复后验证连续运行稳定性。核心原则:重试用于容忍环境抖动,确定性改造用于根治,报告与流程保证 flaky 不被静默忽略。

考察 flaky 治理的系统方法:止血(重试)与根治(确定性改造)分离,回答应覆盖根因分类、报告机制与 CI 修复闭环。

#

50. Playwright 的并行测试(workers)在 CI 的工程性能优化

Playwright 的并行测试(workers)如何在 CI 中实现性能优化?

  • workers 的文件级并行模型
  • 资源配额与分片(sharding)
  • CI 并行下的资源隔离

Playwright 默认按文件粒度并行:每个 worker 进程运行一个测试文件,workers 数量决定同时运行的文件数(可配置 fullyParallel 在文件内也并行)。CI 性能优化核心是让 workers 数量匹配机器核数与浏览器实例容量:CPU 密集的截图/长流程占满核时提高并发收益有限,反而造成资源争抢与 flaky;建议按 CI runner 规格设置 workers,观察 CPU/内存曲线调整。

规模化实践:多台 runner 用 sharding(--shard=x/y)把测试文件分片并行,缩短总时长;每个 worker 独立浏览器上下文与 trace 目录避免文件冲突;数据库/端口等外部资源按 worker 索引隔离或使用独立环境;缓存浏览器二进制与依赖避免重复下载。优化目标不是无限增加并发,而是在"总时长、资源成本、稳定性"三者间取得平衡。

考察并行执行的实际调优:文件级并行模型、workers 与资源匹配、分片与隔离,回答应强调"匹配资源而非盲目并发"。

#

51. Cypress 的 cy.session 在测试间状态复用的工程应用

Cypress 的 cy.session 如何在测试间复用状态?有哪些工程应用?

  • cy.session 缓存登录等会话状态
  • 与 beforeEach 的配合及清理
  • 复用状态的边界与风险

cy.session(name, setup) 把登录、cookie、localStorage 等初始化逻辑缓存为具名会话:首次执行 setup 建立状态,后续测试若缓存有效则直接恢复(注入 cookie/存储),避免每个用例重复走登录流程,显著缩短 E2E 耗时。典型用法:beforeEach 中 cy.session('standard-user', () => cy.login('alice')),测试体直接进入业务页面。

工程应用与边界:按角色/权限建立多个具名会话(普通用户、管理员)矩阵复用;会话恢复依赖浏览器上下文(同一 origin 的 cookie/localStorage),跨域或状态变化(如 token 过期)时需失效策略(validate 函数或 cy.session 的 cacheAcrossSpecs 控制);注意"复用"不等于"共享可变状态"——会话内数据若有用例级修改,应在测试内隔离或重建会话。合理设计 session 可把 E2E 的重复登录成本降低一个数量级。

考察会话复用机制:缓存登录状态、具名会话矩阵、失效与清理策略,回答应强调复用边界与数据隔离。

#

52. Playwright 的 test.use 在多配置(设备、权限)测试的工程实践

Playwright 的 test.use 在多配置(设备、权限)测试中有哪些工程实践?

  • test.use 的配置注入(视口、权限、存储状态)
  • 与 projects 组合的多矩阵测试
  • 配置隔离与覆盖优先级

test.use({ ... }) 在测试文件或 describe 内注入浏览器上下文配置:viewport(移动端视口)、permissions(如 geolocation、notifications)、storageState(登录态)、colorScheme、locale 等,实现"同一测试逻辑、不同设备/权限环境"的矩阵化执行。与 projects 配合时,projects 定义浏览器与全局配置,test.use 在文件/describe 级覆盖,形成"全局基线 + 局部定制"的配置层次。

实践要点:设备矩阵(移动/桌面视口)与权限场景(授权/拒绝通知)用 test.use 声明式表达,避免在每个用例中重复设置;登录态通过 storageState 注入而非 UI 登录;注意配置优先级(项目级 < 文件级 < describe 级)与继承语义,避免配置漂移。多配置测试大幅提升覆盖而控制用例数量,是"参数化测试"在 E2E 层的体现。

考察浏览器上下文的配置化测试:test.use 注入视口/权限/存储状态,与 projects 形成配置层次,回答应说明矩阵化收益与优先级。

#

53. a11y testing(axe-core、jest-axe)

axe-core 与 jest-axe 如何用于 a11y testing?最佳实践是什么?

  • jest-axe 的 toHaveNoViolations 断言
  • 在单测与 E2E 中的集成方式
  • 结果解读(violations/incomplete)与修复闭环

axe-core 提供 WCAG 规则引擎,jest-axe 将其封装为 Jest/Vitest 匹配器:expect(screen.getByRole('main')).toHaveNoViolations(),在单元/组件测试中对渲染结果运行完整规则检查。E2E 场景用 @axe-core/playwright 或 Cypress axe 插件,对真实页面全量检查。断言失败时返回 violations 数组(规则名、影响等级、节点与修复建议)与 incomplete(无法自动判定、需人工确认的项目)。

最佳实践:在组件测试中为每个组件(尤其是表单、弹窗、导航)添加 a11y 断言,作为可访问性的持续回归网;E2E 中对关键页面做整页检查并设置严重级别过滤(critical/serious 必为零);把 violations 接入缺陷流程(自动开 issue)实现修复闭环;定期人工复核 incomplete 项目。注意 axe 不能验证键盘操作体验与真实读屏行为,需与人工测试结合。

考察 a11y 自动化测试的工具链:jest-axe 断言、E2E 集成与结果分级,回答应体现自动化与人工的分层及修复闭环。

#

54. Storybook Test Runner 在 CI 的组件快照测试与现代应用

Storybook Test Runner 如何在 CI 中做组件快照与交互测试?有哪些现代应用?

  • Test Runner 基于 Playwright 的 story 遍历
  • 快照与交互测试的 CI 集成
  • 与视觉回归、单测的互补

Storybook Test Runner 基于 Playwright 遍历所有 story 并在真实浏览器中执行:默认对每个 story 做 smoke test(渲染无错误),配置 test-runner 的 postVisit 钩子可实现 DOM 快照比对(HTML snapshot)与自定义断言;集成 jest-axe 可对每个 story 做 a11y 检查。CI 中与视觉回归(Chromatic)并行,形成"渲染正确 + 快照稳定 + 交互通过"的组件质量层。

现代应用:把 Test Runner 与 Vitest plugin 结合——前者跑真实浏览器冒烟与快照、后者跑快速单测;通过 tags(如 'skip')控制 story 的测试范围;快照更新走审查流程防止无意识漂移。边界:DOM 快照对结构变化敏感,适合稳定组件库;与像素级视觉回归互补(快照抓结构、视觉抓外观),两者共同覆盖组件回归风险。

考察 Storybook Test Runner 的能力与定位:真实浏览器冒烟、DOM 快照、a11y 检查,回答应说明其与视觉回归和单测的分工。

#

55. Playwright 的网络限速(throttling)在真实网络模拟的工程价值

Playwright 的网络限速(throttling)如何模拟真实网络?有何工程价值?

  • CDP 网络模拟:带宽、延迟与离线
  • 慢网络下的加载态与容错测试
  • 与性能预算测试的协作

Playwright 通过 context.route 的 abort/fulfill 与 CDP 网络域(context.newCDPSession 的 Network.emulateNetworkConditions)实现网络模拟:可设置带宽、RTT 延迟、丢包与离线状态,模拟 3G/4G、慢速 Wi-Fi 等场景。工程价值:一是验证慢网下的 UX——加载骨架、超时重试、错误提示是否正常;二是发现真实网络依赖问题(资源加载顺序、请求并发、大包体导致的卡顿);三是与性能测试协作:固定网络条件后测量 LCP/INP 等指标,保证性能基线可复现。

实践要点:限速配置放在 fixture 或 test.use 中按场景启用;注意模拟网络只影响该上下文,不影响其他并行 worker;结合 trace 观察请求时间线定位瓶颈。边界:CDP 模拟粒度是整页网络而非单请求,精细化场景(单资源延迟)用 route 的 setTimeout/fulfill 延迟实现。

考察网络模拟的能力与价值:带宽/延迟/离线模拟支撑慢网 UX 验证与可复现的性能测量,回答应包含配置方式与边界。

#

56. Cypress 的录制(cypress 录制)模式在测试编写的工程实践

Cypress 的录制模式在测试编写中有哪些工程实践?

  • 录制(record/replay)生成测试代码
  • 与手写测试的取舍
  • 录制产物的维护策略

Cypress 的录制能力(Experiment 录制/回放与 Cloud 的录制回放)把用户在浏览器中的真实操作录制为 Cypress 命令序列,可作为测试初稿;生成代码后再人工补充断言(数据校验、UI 状态、请求断言)与稳定性处理(选择器语义化、等待策略)。录制的价值是快速覆盖陌生流程、还原 bug 现场(回放重现缺陷),而非直接产出生产级测试。

实践要点:录制产物必须人工重构——把硬编码选择器改为 getByRole/getByLabelText 语义定位、提取可复用命令(cy.login 等自定义命令)、删除冗余步骤、添加数据隔离;录制主要用于"探索与文档化"(记录复现步骤),生产测试仍以手写 + 评审为主。录制与手写是互补关系:录制降低起步成本,手写保证可维护性,两者结合提升 E2E 编写效率。

考察录制模式的定位:录制作为测试初稿与 bug 复现工具,人工重构与语义化是关键,回答应强调录制与手写的互补。

#

57. 并行测试的资源隔离,测试数据库/端口/文件系统/浏览器上下文的隔离方案在 CI 并行执行下的工程实践?

并行测试中,测试数据库、端口、文件系统与浏览器上下文如何隔离?CI 并行执行下有哪些工程实践?

  • 数据库隔离:事务回滚、独立 schema、唯一租户
  • 端口与文件系统的冲突规避
  • 浏览器上下文与 worker 的隔离

并行测试的资源隔离分四层:数据库——每 worker 用独立 schema/数据库实例、或用事务回滚(测试结束回滚不留痕)、或按 worker 索引生成唯一租户标识(数据互不干扰),禁止共享唯一主键或固定种子;端口——动态分配端口(服务绑定 0 或从池中取)替代硬编码,避免端口占用冲突;文件系统——测试产物写入按 worker 隔离的目录(PID/索引后缀),避免同名文件互踩;浏览器上下文——Playwright/Cypress 每个 worker 独立上下文(cookie、存储、localStorage 天然隔离),登录态互不影响。

CI 实践:限制 workers 数匹配可用资源(数据库连接池、CPU);重试机制与资源清理挂钩(afterAll 清库、删临时文件);共享资源(如 CDN、认证服务)只读化或单独 mock;监控资源争抢指标(连接数、磁盘 IO),flaky 出现时优先排查隔离缺陷而非加大重试。核心原则:每个 worker 拥有"可独享、可清理、可重建"的完整环境。

考察并行执行的根本难点——共享资源冲突,回答应按数据库/端口/文件系统/上下文四层给出隔离方案与 CI 配套实践。