goldbergyoni /javascript-testing-best-practices
📗🌐 🚢 Comprehensive and exhaustive JavaScript & Node.js testing best practices (August 2025)
★ 24,610⑂ 2,079
24,610
stars
+3+4+-1
2,079
forks
–
watchers
–
prs
–
issues
–
releases
–
discussions
README
👇 为什么本指南可以助你将测试能力提升到下一层级
📗 45+ 最佳实践:非常全面彻底
这篇文章从 A 到 Z 给出了 JavaScript & Node.js 的稳定性指南。它为你整理总结了市面上大量的最佳博客文章、书籍以及工具。
🚢 进阶:在基础上前进 10000 公里
从基础领域跨上前往进阶话题的旅程,包括:在生产环境测试、编译测试、基于属性的测试以及很多策略 & 专业工具。如果你仔细阅读了本指南中的每个字,则你的测试能力将有可能大大超过平均水平。
🌐 全栈:前端、后端、CI、任何岗位
先了解通用的测试实践为其他应用层的打下基础。然后,在你自己的领域深入探索:前端/UI、后端、CI 甚至是他们所有的层面。
作者 Yoni Goldberg
- 一位 JavaScript & Node.js 顾问
- 👨🏫 我的测试网站 - 在欧洲 & 美国了解 我的测试网站
- 在 Twitter 关注我
- 来 LA, Verona, Kharkiv, free webinar听我的演讲。后续工作待定。
- 我的 JavaScript 质量新闻 - 战略层面的视野和信息
内容列表
第 0 章:黄金法则
一条启发所有人的建议(特殊的 1 条)
第一章:测试剖析
基础 - 搭建干净的测试(12 条)
第二章:后端测试
高效地编写后端和微服务的测试(8 条)
第三章:前端测试
为 UI(包括组件和 E2E 测试)编写测试(11 条)
第四章:度量测试效果
度量测试质量(4 条)
第五章:持续集成(CI)
JS 领域的 CI 指南(9 条)
第 0 章:黄金法则
⚪️ 0 黄金法则:设计瘦测试
:white_check_mark: 建议: 测试代码与生产代码不同,要使它变得极其简单、短小、没有抽象、扁平化、使人愉悦、瘦。一段测试代码需要做到让人一眼就能看出其目的。
我们的思维空间被主体生产代码充满,因此无法腾出额外的“大脑空间”存放复杂的东西。如果向可怜的大脑中塞进其他复杂代码,将会使得整个部分变慢,而这个部分正是用来解决我们需要测试的问题的。这也是大部分团队放弃测试的原因。
另一方面,测试是一个友好的助手,一个你乐于与之合作、投资小回汇报大的助手。科学证明我们有两套大脑系统:系统 1 用于无需努力的活动如在一个空旷的路上开车;系统 2 用于复杂和繁琐的工作如算一道数学表达式。将你的测试为系统 1 设计,当你看一段测试代码时,需要像改 HTML 文档一样简单而不是像计算 2 × (17 × 24)。
为了达到这个目的,我们可以通过选择性价比高、投入产出比(ROI)高的技术、工具以及测试对象。仅测试需要的内容,努力保持其灵活性,某些时候甚至值得去舍弃一些测试来换取灵活性和简洁性。
!alt text
下面的大部分建议衍生自这一原则。
准备好开始了吗?
第一章: 测试剖析
⚪ ️ 1.1 每个测试用例的名称必须包含三个部分
:white_check_mark: 建议: 一个测试报告需要让不熟悉代码的人(测试、运维)明确知道新的变更是符合需求。因此测试名称需要从需求层面描述,并且包含三个部分:
(1) 被测的是什么?(比如 ProductsService.addNewProduct 方法)
(2) 在什么条件和场景下?(比如没有 向该方法传入 price 参数)
(3) 期望的结果是什么?(比如不允许添加该产品)
❌ 否则: 当一个名为“新增产品”的测试用例挂掉之后,你如何准确找到是哪里出问题了?
👇 Note: 每一条后面会有一个 代码示例,有时候还会放一张图片说明。
✏ 代码示例
:clap: 正例: 一个包含三部分的用例名
!
javascript
//1. unit under test
describe('Products Service', function() {
describe('Add new product', function() {
//2. scenario and 3. expectation
it('When no price is specified, then the product status is pending approval', ()= {
const newProduct = new ProductService().add(...);
expect(newProduct.status).to.equal('pendingApproval');
});
});
});
:clap: 正例: 一个包含三部分的用例名
!alt text
⚪ ️ 1.2 使用 AAA 模式构造测试内容
:white_check_mark: 建议: 将你的测试内容划分为三个部分:布置,执行,断言 —— Arrange, Act & Assert (AAA)。这样读者就无需动用脑细胞理解你的测试内容了:
1st A - 准备(Arrange):一些用于提供上下文的代码。可能包含:构造数据、添加 DB 记录、mocking/stubbing 对象,以及其他的准备代码;
2nd A - 执行(Act):执行测试单元。通常一行代码。
3rd A - 断言(Assert):保证得到的值符合预期。通常一行代码。
❌ 否则: 你不仅要花大量时间理解这段代码,而且本该是最简单的部分却耗费了你的大量脑细胞。
✏ 代码示例
:clap: 正例: 一个使用 AAA 模式构造的测试用例
! !
javascript
describe('Customer classifier', () = {
test('When customer spent more than 500$, should be classified as premium', () = {
//Arrange
const customerToClassify = {spent:505, joined: new Date(), id:1}
const DBStub = sinon.stub(dataAccess, "getCustomer")
.reply({id:1, classification: 'regular'});
//Act
const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
//Assert
expect(receivedClassification).toMatch('premium');
});
});
:thumbsdown: 反例: 没有分隔、一大坨、难以解释
j