Skip to content

代码质量面试题

基础题

1. ESLint 和 Prettier 有什么区别?基础

参考答案:

ESLint 与 Prettier 是前端代码质量体系中两个不同定位的工具:

  • ESLint:静态代码分析工具,核心职责是发现潜在错误、约束编码风格、保证代码质量。它可以检测未使用变量、可能的空指针、不可达代码、变量提升误用等问题,也能通过自定义规则约束团队规范。
  • Prettier:代码格式化工具,核心职责是统一代码外观(缩进、换行、引号、分号等)。它不关注代码逻辑正确性,只关心“代码看起来一致”。

协作方式:ESLint 负责“对不对”,Prettier 负责“好不好看”。两者规则可能冲突(如 max-len 与 Prettier 换行),通常用 eslint-config-prettier 关闭 ESLint 中与格式冲突的规则,再用 eslint-plugin-prettier 把 Prettier 作为一条 ESLint 规则运行。

npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier

评分维度

  • 概念区分准确(40%)
  • 能举例说明各自职责(30%)
  • 知道如何配合与解决冲突(30%)

2. Husky 和 lint-staged 的作用是什么?基础

参考答案:

  • Husky:Git 钩子管理工具,可在 pre-commitcommit-msgpre-push 等时机执行脚本。它把质量检查左移到提交阶段,避免不符合规范的代码进入仓库。
  • lint-staged:只对当前 git add 暂存区的文件运行指定命令。它解决了“每次提交都 lint 整个项目”的性能问题,让提交前检查聚焦在改动文件上。

典型配合:在 pre-commit 钩子中运行 lint-staged,对改动的 .js/.ts/.vue 等文件执行 ESLint + Prettier;在 commit-msg 钩子中校验提交信息是否符合 Conventional Commits。

{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"]
  }
}

评分维度

  • 能解释 Husky 的 Git 钩子作用(30%)
  • 能解释 lint-staged 的增量检查作用(30%)
  • 能说明两者配合的典型场景(40%)

3. 单元测试和 E2E 测试有什么区别?基础

参考答案:

维度 单元测试 E2E 测试
测试对象 最小可测单元(函数、组件、Hook) 完整用户流程
依赖 大量 Mock,隔离外部依赖 模拟真实浏览器/设备环境
速度 毫秒级,可频繁运行 秒级到分钟级
定位能力 失败时定位精准 失败时需排查链路
工具 Jest、Vitest、Mocha Cypress、Playwright、Selenium

举例:一个表单提交功能,单元测试验证 validatePhone(phone) 对各类手机号的校验结果;E2E 测试则模拟用户输入、点击提交、看到成功提示的完整流程。

两者应形成测试金字塔:大量单元测试打底,适量集成测试衔接,少量 E2E 覆盖核心路径。

评分维度

  • 能清晰区分测试范围(30%)
  • 能对比速度、成本、定位能力(30%)
  • 能举例说明并提到测试金字塔(40%)

4. 什么是 Mock?为什么需要 Mock?基础

参考答案:

Mock 是用可控的“假对象”替代真实依赖的技术。它让测试聚焦在被测单元本身,而不受外部环境影响。

需要 Mock 的典型原因:

  1. 隔离性:避免后端接口、数据库、第三方服务不可用导致测试失败。
  2. 确定性:模拟边界值、异常状态、网络超时等难以在真实环境复现的场景。
  3. 速度:真实 HTTP 请求慢,Mock 后测试可在毫秒级完成。
  4. 可重复性:每次运行环境一致,避免 flaky test。

示例(Jest):

jest.mock('./api', () => ({
  fetchUser: () => Promise.resolve({ id: 1, name: 'Alice' })
}));

Mock 应遵循“只 Mock 跨越边界的外部依赖”,不要 Mock 被测函数的内部实现,否则测试会失去意义。

评分维度

  • 能解释 Mock 概念(30%)
  • 能说明 3 个以上使用原因(40%)
  • 能写出示例并知道使用边界(30%)

5. Conventional Commits 中常见的 type 有哪些?基础

参考答案:

Conventional Commits 通过规范提交信息自动生成 changelog 和版本号。常见 type:

  • feat:新功能,对应 minor 版本升级。
  • fix:Bug 修复,对应 patch 版本升级。
  • docs:文档变更,不影响产物。
  • style:代码格式修改,如空格、分号,不影响逻辑。
  • refactor:重构,既不新增功能也不修复 Bug。
  • perf:性能优化。
  • test:测试相关变更。
  • chore:构建、工具、依赖等杂项。
  • ci:CI/CD 配置变更。
  • BREAKING CHANGE:破坏性变更,对应 major 版本升级。

示例:feat(auth): add OAuth2 login

评分维度

  • 能说出 5 个以上 type(40%)
  • 能区分 feat/fix/BREAKING CHANGE 与版本号关系(30%)
  • 能写出规范提交信息示例(30%)

进阶题

6. 如何配置 ESLint 和 Prettier 避免冲突?进阶

参考答案:

避免冲突的核心思路是:Prettier 管格式,ESLint 管质量;关闭 ESLint 中与格式冲突的规则,并让 Prettier 以 ESLint 规则形式运行。

步骤:

  1. 安装依赖:
    npm i -D eslint prettier eslint-config-prettier eslint-plugin-prettier
    
  2. 在 ESLint 配置中 extends 最后加入 'prettier',关闭冲突规则:
    // eslint.config.js (Flat Config)
    import js from '@eslint/js';
    import prettier from 'eslint-config-prettier';
    import prettierPlugin from 'eslint-plugin-prettier';
    
    export default [
      js.configs.recommended,
      {
        plugins: { prettier: prettierPlugin },
        rules: {
          'prettier/prettier': 'error',
          // 其他业务规则
        }
      },
      prettier // 放最后,关闭与 Prettier 冲突的规则
    ];
    
  3. 配置 .prettierrc 统一格式规则:
    {
      "semi": true,
      "singleQuote": true,
      "trailingComma": "es5"
    }
    
  4. lint-staged 中先 prettier --writeeslint --fix,避免循环修复:
    {
      "lint-staged": {
        "*.{js,ts,tsx}": ["prettier --write", "eslint --fix"]
      }
    }
    

评分维度

  • 能说明职责分离思路(25%)
  • 能写出关键 ESLint 配置(35%)
  • 能说明执行顺序和 lint-staged 配合(25%)
  • 了解 Flat Config 等新趋势(15%)

7. 测试覆盖率 100% 是否代表没有 Bug?进阶

参考答案:

不是。覆盖率只衡量代码被执行的程度,不衡量测试是否有效。

覆盖率无法保证的情况:

  • 测试没有断言,仅调用函数。
  • 边界条件、异常分支未覆盖。
  • 并发、时序、兼容性场景未验证。
  • 业务逻辑本身理解错误。

高质量测试应关注:

  • 核心路径与边界条件。
  • 输入输出的等价类划分。
  • 异常与降级路径。
  • 行为是否符合需求,而非盲目追求数字。

覆盖率是有用指标,但应作为“健康度参考”,而不是唯一目标。建议设定合理阈值(如核心模块 80%),并重点审查未覆盖代码的合理性。

评分维度

  • 能明确回答不是(20%)
  • 能解释覆盖率的局限(40%)
  • 能提出高质量测试的标准(40%)

8. 什么是 TDD?它的核心流程是什么?进阶

参考答案:

TDD(Test-Driven Development,测试驱动开发)是一种“先写测试、再写代码”的开发方式,核心循环为 红-绿-重构

  1. :写一个失败的测试,明确当前要实现的行为。
  2. 绿:编写最小代码让测试通过,不追求优雅。
  3. 重构:在测试保护下优化代码结构,保持测试通过。

价值:

  • 提前明确需求接口,减少返工。
  • 每个功能都有测试守护,便于后续重构。
  • 设计更模块化,因为难测试的代码往往设计也不好。

示例:先写 add(1, 2) === 3 的测试,再实现 add 函数。

TDD 适合业务逻辑清晰、输入输出明确的模块;对 UI 变化频繁、需求不确定的探索性开发,可灵活运用而非教条执行。

评分维度

  • 能解释 TDD 概念(30%)
  • 能说清红绿重构循环(40%)
  • 能说明价值与适用边界(30%)

9. Code Review 应该关注哪些方面?进阶

参考答案:

一次高质量的 Code Review 应从多个维度检查:

  1. 正确性:是否满足需求,边界与异常是否处理。
  2. 可读性:命名、注释、结构是否清晰,是否易于理解。
  3. 可维护性:是否遵循单一职责,是否存在重复代码。
  4. 可测试性:逻辑是否解耦,是否方便写测试。
  5. 性能:是否存在明显低效实现,如重复计算、大数据量遍历。
  6. 安全性:是否有 XSS、CSRF、敏感信息泄露风险。
  7. 兼容性:对不同浏览器、设备、降级场景是否考虑。
  8. 规范符合度:是否遵循团队的 ESLint、Git、架构规范。

Review 时应注意区分“阻塞性问题”与“建议性意见”,避免吹毛求疵;同时给予正面反馈,营造学习型文化。

评分维度

  • 能覆盖 5 个以上维度(40%)
  • 能说明 Review 的目的与价值(30%)
  • 提到阻塞性问题与建设性反馈的平衡(30%)

10. Git 工作流有哪些?如何选择?进阶

参考答案:

常见 Git 工作流:

  • Git Flow:拥有 maindevelopfeaturereleasehotfix 分支,适合有明确版本发布节奏的项目。
  • GitHub Flow:基于 main 分支,功能通过短期 feature branch + PR 合并,适合持续部署、轻量团队。
  • GitLab Flow:结合 issue 与 MR,支持环境分支(如 pre-productionproduction),适合多环境发布。
  • Trunk-Based Development:所有开发者直接向主干提交或短期分支快速合并,配合功能开关,适合高频发布和大规模团队。

选择依据:

  • 发布频率:高频发布选 GitHub Flow / Trunk-Based。
  • 版本管理:需要版本号选 Git Flow。
  • 团队规模与 CI 成熟度:大团队、强 CI 更适合 Trunk-Based。

评分维度

  • 能说出 3 种以上工作流(40%)
  • 能对比各自特点(30%)
  • 能给出选型依据(30%)

高级题

11. 如何设计一个前端项目的质量门禁?深入

参考答案:

质量门禁应贯穿软件生命周期,形成“不合格代码无法前进”的自动化屏障:

  1. 提交前:Husky + lint-staged 运行 ESLint/Prettier,拦截格式与基本质量问题。
  2. 提交信息:commitlint 校验 Conventional Commits。
  3. PR 阶段:CI 运行单元测试、类型检查、构建检查;Code Review 至少一名核心成员通过。
  4. 合并前:测试覆盖率门禁、SonarQube 质量阈值、依赖安全扫描(npm audit / Snyk)。
  5. 构建阶段:产物体积预算、构建失败告警。
  6. 部署前:灰度环境验证、E2E 回归通过。
  7. 上线后:错误监控、性能监控、业务指标异常告警。

关键点:门禁规则要适度,过严会降低开发效率,过松则失去意义;应根据团队成熟度动态调整。

评分维度

  • 能覆盖 4 个以上阶段(40%)
  • 能说明每个阶段的具体手段(30%)
  • 能提到动态调整与平衡(30%)

12. 如何提高团队的代码质量意识?深入

参考答案:

提高代码质量意识不能仅靠制度,更要靠文化与工具:

  1. 建立清晰规范:编码规范、Review 指南、架构原则文档化,让大家知道“好代码长什么样”。
  2. 工具化检查:ESLint、Prettier、类型检查、CI 门禁,把规范变成自动约束,降低人为遗漏。
  3. 营造 Code Review 文化:Review 不是挑刺,而是互相学习;资深成员带头示范。
  4. 定期技术分享:分享 bad case、重构案例、性能/安全专题,让团队看到质量问题的真实影响。
  5. 质量指标可视化:在团队看板展示覆盖率、Bug 率、线上故障数,形成正向激励。
  6. 预留重构时间:把技术债清理纳入迭代计划,而不是临时救火。
  7. 领导以身作则:负责人主动参与 Review、接受反馈、承认不足。

评分维度

  • 能提出 4 个以上可落地方法(40%)
  • 能说明文化与工具结合(30%)
  • 能结合实际场景说明(30%)

13. 什么是技术债务?如何管理?深入

参考答案:

技术债务是为了快速交付而有意或无意做出的技术妥协,长期会带来维护成本上升、开发效率下降、稳定性风险增加。

管理方式:

  1. 识别与记录:建立技术债清单,说明位置、原因、影响、预估修复成本。
  2. 分类分级:按影响程度分为高/中/低,优先偿还架构债和安全债。
  3. 量化影响:用数据说明债务导致的 Bug 率、上线时间、重构成本。
  4. 纳入迭代:每个迭代预留 10%–20% 时间用于还债。
  5. 先止血再重构:补充测试和监控后再动核心代码,避免引入回归。
  6. 防止新增债务:通过 Code Review、Lint、架构评审拦截低质量代码。
  7. 定期复盘:每月/每季度回顾技术债清单,跟踪偿还进度。

评分维度

  • 能解释技术债务概念(30%)
  • 能说出 4 个以上管理方法(40%)
  • 能提到量化与防止新增债务(30%)

14. Playwright 和 Cypress 各有什么优缺点?深入

参考答案:

维度 Cypress Playwright
浏览器支持 主要 Chromium/Firefox/WebKit 支持有限 Chromium/Firefox/WebKit 原生支持
并发能力 单线程限制,并行需付费 Dashboard 原生多 worker 并行,速度快
多标签页/iframe 较弱
调试体验 时间旅行、截图、视频回放优秀 调试能力持续提升,Trace Viewer 好用
生态 成熟,插件丰富 增长快,微软背书
语言 主要 JS/TS JS/TS/Python/Java/.NET

选型建议:

  • 团队以 JS/TS 为主、重视调试体验、测试场景相对简单 → Cypress。
  • 需要跨浏览器、多标签页、高并行、多语言支持 → Playwright。

评分维度

  • 能分别说出优缺点(40%)
  • 能从至少 3 个维度对比(30%)
  • 能给出选型建议(30%)

15. 如何平衡代码质量和交付速度?深入

参考答案:

代码质量与交付速度不是对立关系,好的质量往往能提升长期交付速度。平衡策略:

  1. 质量左移:在需求评审、设计阶段就识别风险,减少后期返工。
  2. 自动化质量门禁:用 CI 自动跑 lint、测试、类型检查,减少人工审查时间。
  3. 核心路径严格、边缘路径灵活:关键业务和公共模块必须高质量;实验性需求允许技术债,但要有偿还计划。
  4. 小步快跑、持续重构:避免一次性大重构,把改进分散到日常迭代。
  5. 避免过度工程:不为未来不确定的需求做复杂抽象。
  6. 度量和反馈:用 Bug 率、返工率、上线时长等指标评估是否失衡。

评分维度

  • 能说明两者不是对立关系(20%)
  • 能给出 4 个以上平衡方法(50%)
  • 能结合实际场景说明(30%)

补充题

16. 什么是 BDD?基础

参考答案:

BDD(Behavior-Driven Development,行为驱动开发)是一种以业务行为为核心的开发方法。它要求用自然语言(Given-When-Then)描述用户场景,促进开发、测试、产品、业务人员使用统一语言。

与 TDD 的关系:

  • TDD 关注“代码是否正确”,从技术测试出发。
  • BDD 关注“业务行为是否被满足”,从用户场景出发。

示例(Cucumber):

Feature: 用户登录
  Scenario: 使用正确账号密码登录
    Given 用户已注册
    When 用户输入正确账号密码并提交
    Then 用户应进入首页

BDD 适合需求复杂、跨角色协作频繁的项目,能帮助团队对齐理解。

评分维度

  • 能解释 BDD 概念(40%)
  • 能与 TDD 区分(30%)
  • 能写出 Given-When-Then 示例(30%)

17. 如何处理前端项目中的测试 flaky?基础

参考答案:

Flaky test 是指结果不稳定、时而通过时而失败的测试。常见原因包括异步等待、外部依赖、共享状态、时序问题。

处理方法:

  1. 避免固定 sleep:使用 waitFor、元素可见性、网络空闲等确定性等待条件。
  2. 隔离测试数据:每个测试用独立账号、独立数据,避免互相影响。
  3. Mock 不稳定外部依赖:接口、第三方服务、时间函数用 Mock 替代。
  4. 重试机制:CI 中配置失败重试,但不应作为根本解决方案。
  5. 检查异步操作:确保 async/await、Promise 处理正确。
  6. 减少全局状态污染:测试间清理 localStorage、DOM、事件监听。
  7. 根因分析:对高频 flaky 用日志、截图、视频定位,彻底修复而非简单重试。

评分维度

  • 能解释 flaky 测试的危害(30%)
  • 能给出 4 种以上解决方法(50%)
  • 能强调根因分析而非简单重试(20%)

18. 什么是 Mutation Testing?基础

参考答案:

Mutation Testing(变异测试)是一种评估测试有效性的方法。它通过自动修改源代码中的小部分逻辑(如把 > 改成 <true 改成 false、删除方法调用、修正常量值),生成大量“变异体”,然后运行测试。

如果测试能发现变异体(即测试失败),称为“变异体被杀死(killed)”,说明测试有效;如果变异体“存活(survived)”,说明测试用例存在盲区。

价值:

  • 比单纯覆盖率更能反映测试质量。
  • 发现测试断言不足、边界缺失、弱断言等问题。
  • 推动开发者写出更高质量的测试。

工具:Stryker(JS/TS)、PIT(Java)、MutPy(Python)。

注意:变异测试运行成本高,通常用于核心模块、关键算法或安全敏感代码,而不是全量跑。建议结合 CI 定期执行或作为发布前的质量门禁之一。

评分维度

  • 能解释变异测试概念(35%)
  • 能说明“变异体存活/被杀死”的含义(30%)
  • 能说明价值与使用边界(25%)
  • 能列举工具(10%)

19. 如何评估一个项目的测试策略是否合理?基础

参考答案:

合理的测试策略应满足“分层合理、覆盖关键、运行稳定、成本可控”。评估维度:

  1. 测试金字塔结构:单元测试是否占主体,E2E 是否聚焦核心路径。
  2. 核心功能覆盖:关键业务逻辑、公共组件、工具函数是否有测试守护。
  3. 关键用户路径覆盖:登录、下单、支付等核心流程是否有 E2E。
  4. 运行速度与稳定性:CI 反馈是否在可接受范围,flaky 比例是否低。
  5. 覆盖率趋势:是否合理且持续改进,而非盲目追求 100%。
  6. 可维护性:测试是否易于理解、修改,Mock 是否适度。
  7. 与发布流程集成:失败是否阻止合并/发布。

评分维度

  • 能说出 4 个以上评估维度(40%)
  • 能结合测试金字塔说明(30%)
  • 能提出改进方向(30%)

20. 你认为好的代码应该具备哪些特征?基础

参考答案:

好的代码不仅是“能跑”,还要“好懂、好改、好测”。主要特征:

  1. 可读性强:命名清晰、结构直观、注释解释“为什么”而非“做什么”。
  2. 职责单一:每个函数/模块只做一件事,低耦合高内聚。
  3. 易于测试:输入输出明确,依赖可注入,避免隐式全局状态。
  4. 可扩展性:通过接口、配置、插件机制支持未来变化,而非硬编码。
  5. 错误处理完善:边界条件、异常路径、用户输入校验都有考虑。
  6. 性能合理:不提前优化,但避免明显的低效实现。
  7. 符合规范:遵循团队 ESLint、架构约定和代码风格。
  8. 可观测性:关键路径有日志、埋点或监控,便于排查问题。

评分维度

  • 能说出 4 个以上特征(40%)
  • 能结合实际说明(30%)
  • 能体现系统性质量思维(30%)

21. Biome / Oxc 与传统 ESLint + Prettier 相比有什么优势和局限?基础
22. 什么是 Codemod?什么时候应该使用?基础
23. 什么是属性测试(Property-Based Testing)?举一个 fast-check 的例子。基础
24. 什么是架构测试?如何用 ts-arch 约束模块依赖?基础

基于 MIT 协议发布