使用 Jest 进行 JavaScript 测试驱动开发 (TDD) 的基础知识
[JS#4 WIL 🤔 帖子]
测试驱动开发 (TDD) 的核心思想是在编写待测试代码之前,通过编写自动化测试来开始代码开发。JavaScript 中有很多测试运行系统: Jasmine、Jest、Tape和Mocha等等。它们各有特色,但语法非常相似。选择框架应该不成问题,因为
编写测试与语法无关,而更多地与 TDD 哲学有关,
所以我尝试用 Jest 来内化这些概念。我做这个练习的主要目标是了解测试的意义和目的。
在深入探讨之前,以下是我从这场精彩的演讲《测试的魔力》中摘取的一些笔记。
- 为什么大多数开发人员讨厌测试?因为它们缓慢、脆弱,而且耗时。
- 删除一些测试是完全有效的。
- 单元测试目标:它们必须彻底(我们希望它们能够从逻辑上、完整地证明被测单个对象的行为正确)并且稳定(我们不希望每次实现细节发生变化时都破坏测试😟),快速且少量(为最简约的表达式编写测试[mmmmmm🤔])。
- 不要测试私有方法。但如果在开发过程中可以节省成本,可以打破这条规则。
- 模拟是测试替身,它扮演真实应用中某个对象的角色。确保测试替身与 API 保持同步。
- 相信合作者会做正确的事。坚持简单。
- 提高测试水平需要时间和练习。
被测对象有三个消息来源:
📌传入- 从外部发送给对象的消息
📌自身- 被测对象发送给自身的消息
📌传出- 对象发送给外部的消息。
消息有两种类型:查询 (query)和命令 (command)。查询会返回某些内容或不做任何更改。命令类型不返回任何内容但会进行某些更改。
📌 测试规则网格
下面的测试结果网格显示了如何对每种类型的消息进行单元测试。
消息类型 | 询问 | 命令 |
---|---|---|
传入 | 断言结果: 通过断言返回的内容来测试传入的查询消息。 测试接口,而不是实现。 |
通过对直接公开副作用进行断言来测试传入的命令消息。 务必做到“DRY it out”(严格遵守)。传入消息的接收者对断言直接公开副作用的结果负有全部责任。 |
发送给自己 | 忽略:不测试私有方法。 | 忽略:不测试私有方法。 |
传出 | 忽略。传入查询的接收者仅负责涉及状态的断言。 如果消息没有可见的副作用,则发送者不应对其进行测试。 |
期望使用模拟发送传出命令消息 |
📌 TDD 的优势
- 减少添加新功能或修改现有功能时可能引入的错误
- 构建安全网,防止其他程序员的更改影响代码的特定部分
- 通过确保代码在新的更改后仍能正常工作来降低更改成本
- 减少测试人员和开发人员手动(猴子)检查的需要
- 提高对代码的信心
- 减少重构过程中对重大变更的担忧
📌 Jest 入门
Jest 是一个 JavaScript 测试框架,注重简洁性,但仍然确保 JavaScript 代码库的正确性。它以快速、安全、可靠地并行运行测试以及独特的全局状态而闻名。为了加快速度,Jest 会首先运行先前失败的测试,然后根据测试文件运行的时间重新组织运行。
此外,Jest 文档完善,配置要求低。它确实让 JavaScript 测试变得轻松愉快。您可以使用yarn
或进行安装npm
。
📌 TDD 的三种模式
- 明显的实现。由于您知道如何实现要测试的方法,因此您可以使用实现来编写测试。
- 假装成功。如果你知道问题和解决方案,但你无法立即理解如何编写代码,那么你可以使用一个叫做“假装成功”的技巧。
- 三角测量。这是进行 TDD 最保守的方法。如果你甚至不知道解决方案,你就会不惜一切代价地走向绿色,红绿循环。
📌 使用 Jest Matchers
常见匹配器
测试值的最简单方法是完全相等。
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
上面的代码片段返回一个“期望”对象。该toBe(3)
部分是匹配器。Jest 运行时,它会跟踪所有失败的匹配器,以便打印出合适的错误消息。toBe
匹配器用于Object.is
测试是否相等。
真实性
在单元测试中,可能还需要检查特殊值undefined
、null
、 。Jestfalse
包含一些辅助函数,可以让开发人员明确预期结果。因此,最好使用一个与代码实际功能最匹配的匹配器。
toBeNull
仅匹配null
toBeUndefined
仅匹配undefined
toBeDefined
与……相反toBeUndefined
toBeTruthy
if
匹配语句视为真的任何内容toBeFalsy
if
匹配语句视为错误的所有内容
数字
Jest 还提供用于比较数字的匹配器,例如,,,toBeGreaterThan
。对于浮点数,还有像这样的相等匹配器。toBeGreaterThanOrEqual
toBeLessThan
toBeLessThanOrEqual
toBeCloseTo
字符串
可以使用 来根据正则表达式检查字符串toMatch
。
数组和可迭代对象
toContain
可用于检查是否可以在数组或可迭代对象中找到特定项目。
例外
toThrow
可以用来检查某个函数是否抛出了特定的错误。需要注意的是,被检查的函数需要在包装函数中调用,才能使toThrow
异常生效。
还有更高级的 Jest 匹配器用于测试异步代码,即回调和承诺。
📌 Jest 测试实践
这是我第一次使用 Jest 编写 JavaScript 单元测试。这对我来说还很新,所以我需要一些练习😄。我尝试对以下一些方法使用显而易见的实现和三角测量模式进行测试。这些方法的完整实现及其对应的测试可以在我的Jest 实践 GitHub 仓库中找到。
capitalize(string)
接受一个字符串并返回首字符大写的字符串。
const capitalize = require('../capitalize');
test('should capitalize lowercase string correctly', () => {
expect(capitalize("capitalize")).toBe("Capitalize");
});
test("should return '' for strings with length 0", () => {
expect(capitalize("")).toBe("");
});
// other tests here
reverseString(string)
接受一个字符串并将其反转返回。下面是我为正常场景编写的测试代码片段。
const reverseString = require('../reverse-string');
test('should reverse normal strings', () => {
expect(reverseString("reverse")).toBe("esrever");
});
//other tests here
calculator
包含基本运算的对象:、add
、subtract
和divide
。multiply
下面的测试代码片段显示,如果除数为零,该方法将抛出错误消息。
const calculator = require("../calculator");
//other tests here
test("should throw an error if divisor is 0", () => {
expect(() => calculator.divide(20, 0)).toThrow("cannot divide by 0");
});
caesar cipher
凯撒密码是一种替换密码,其中文本中的每个字母都会按字母表向下移动一定位数。更多信息请点击此处。
练习的这一部分需要记住的一点是,不需要显式测试较小的函数,只需测试公共函数即可。如果较大的函数运行良好,那么辅助方法也一定运行良好。
const caesar = require("../caesar-cipher");
//other tests here
test('wraps', function() {
expect(caesar('Z', 1)).toEqual('A');
});
test('works with large shift factors', function() {
expect(caesar('Hello, World!', 75)).toEqual('Ebiil, Tloia!');
});
test('works with large negative shift factors', function() {
expect(caesar('Hello, World!', -29)).toEqual('Ebiil, Tloia!');
});
- 数组分析。此函数接受一个数字数组,并返回一个具有以下属性的对象:
average
、min
、max
和length
。
const analyze = require("../analyze");
const object = analyze([1,8,3,4,2,6]);
test("should return correct average", () => {
expect(object.average).toEqual(4);
});
test("should return correct min", () => {
expect(object.min).toEqual(1);
});
// other tests here
查看此处包含的代码片段的 github 存储库,以获得测试的完整图片。
以上概念和要点是使用 Jest 进行 TDD 的基础知识。还有很多内容需要学习,例如更高级的匹配器、模拟、测试代码的异步部分等等。我还需要学习这些内容,这留到下一篇开发文章再说吧😆。
为持续学习干杯!🍷