测试是什么意思?


在技术术语中测试意味着检查我们的代码是否符合某些预期。例如:给定一些输入,一个名为
“transformer”的函数应返回预期的输出。
有许多类型的测试,很快你就会被术语所淹没,让我们长话短书。测试分为三大类

  • 单元测试

  • 集成测试
  • UI测试
    在这个 Jest 教程中,我们将仅涵盖单元测试,但在文章的最后,你将找到更多用于其他类型测
    试的资源。

    什么是Jest?

    Jest 是一个 JavaScript 测试运行器,即用于创建、运行结构化测试的 JavaScript 库。Jest 作
    为 NPM 包发布,你可以将其安装在任何 JavaScript 项目中。Jest 是目前最受欢迎的测试运行器
    之一,也是 Create React App 的默认选择。

    首先要做的事情:我怎么知道要测试些什么?

    当谈到测试时,即使是简单的代码块也会使初学者瘫痪。最常见的问题是“我怎么知道要测试些
    什么?”
    。如果你正在编写 Web 应用,那么一个好的起点就是测试应用的每个页面和每个用户交
    互。但 Web 应用也由单元代码组成,如函数和模块,也需要进行测试。很多时候有两种情况:

  • 你维护没有测试祖传代码
  • 你必须凭空实现新功能
    该怎么办?对于这两种情况,你可以通过考虑代码来检查,以检查给定函数是否产生预期结果
    **。以下是典型测试流程的样子:

应该怎么办?对于这两种情况,你可以通过将测试看作检查给定函数是否产生预期结果的代码
帮助自己。以下是典型测试流程的样子:

  1. 导入要测试的函数
  2. 给函数输入
  3. 定义期望输出
  4. 检查函数是否按照预期输出
    就是这样。如果你按照这些术语思考,测试不再可怕:输入 - 预期输出 - 断言结果。接下来我们
    还会看到一个方便的工具,用于检查几乎确切的测试内容。现在就动手学习 Jest!

    设置项目

    2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]
    https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 3/14
    与每个 JavaScript 项目一样,你需要一个 NPM 环境(确保在你的系统上安装了 Node)。创建
    一个新文件夹并用以下命令初始化项目:

    1 mkdir getting-started-with-jest && cd $_2 npm init -y

    接下来安装Jest:

    1 npm i jest --save-dev

    我们还需要用配置一个 NPM 脚本,用于从命令行运行我们的测试。打开 package.json 并配置
    名为“test”的脚本以运行Jest:

    1 "scripts": {2 "test": "jest"3 },

    规范和测试驱动开发

    作为开发者,我们都喜欢创意自由。但是当谈到严肃的事情时,大部分时间你都没有那么多的特
    权。通常我们必须遵循规范,即建立的书面或口头描述。
    在本教程中,我们从项目经理那里得到了一个相当简单的规范。一个超级重要的客户端需要一个
    函数来过滤一个对象数组。
    对于每个对象,我们必须检查名为“url”的属性,如果属性的值与给定的术语匹配,那么我们应该
    在结果数组中包含匹配的对象。作为一个精通测试的 JavaScript 开发人员,你想要遵循测试驱动
    开发,这是一个强制在开始编码之前编写失败测试的学科。
    默认情况下,Jest 希望在项目下名为 tests 的文件夹中找到测试文件。创建新文件夹:

    1 cd getting-started-with-jest2 mkdir __tests__

    接下来在 tests 中创建一个名为 filterByTerm.spec.js 的新文件。你可能想知道为什么扩展名是
    “.spec。”。这是一个借用 Ruby 的约定,用于将文件标记为给定功能的规范。
    现在来测试吧!

    测试结构和第一次失败的测试

    2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]
    https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 4/14
    现在创建你的第一次Jest测试。打开 filterByTerm.spec.js 并创建一个测试块:

    1 describe("Filter function", () => {2 // test stuff3 });

    我们的第一个朋友是 describe,一个用于包含一个或多个相关测试的 Jest 方法。每次开始为功
    能编写一套新测试时,都会将其包含在 describe 块中。正如你所看到的,它需要两个参数:一
    个用于描述测试套件的字符串,还有一个用于包装实际测试的回调函数。
    接下来我们将遇到另一个名为 test 的函数,它是实际的测试块:

    1 describe("Filter function", () => {2 test("it should filter by a search term (link)", () => {3 // actual test4 });5 });

    这时我们已准备好编写测试了。请记住,测试是关于输入、功能和预期输出的问题。首先定义一
    个简单的输入,一个对象数组:

    1 describe("Filter function", () => {2 test("it should filter by a search term (link)", () => {3 const input = [4 { id: 1, url: "https://www.url1.dev" },5 { id: 2, url: "https://www.url2.dev" },6 { id: 3, url: "https://www.link3.dev" }7 ];8 });9 });

    接下来定义预期结果。根据规范,测试中的函数应该省略其 url 属性与给定搜索项不匹配的对
    象。我们可以期待例如具有单个对象的数组,给定 “link” 作为搜索项:

    1 describe("Filter function", () => {2 test("it should filter by a search term (link)", () => {3 const input = [4 { id: 1, url: "https://www.url1.dev" },5 { id: 2, url: "https://www.url2.dev" },6 { id: 3, url: "https://www.link3.dev" }7 ];8 const output = [{ id: 3, url: "https://www.link3.dev" }];9 });10 });

    现在准备编写实际的测试。我们将使用 expect 和一个 Jest matcher 来检查这个函数在调用时返
    回的预期结果。这是测试:

    1 expect(filterByTerm(input, "link")).toEqual(output);

    为了进一步细分,你可以在代码中调用函数:
    2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]
    https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 5/14

    1 filterByTerm(inputArr, "link");

    在 Jest 测试中,你应该将函数调用包含在 expect 中,它与匹配器(用于检查输出的Jest函数)
    一起进行实际测试。这是完整的测试:

    1 describe("Filter function", () => {2 test("it should filter by a search term (link)", () => {3 const input = [4 { id: 1, url: "https://www.url1.dev" },5 { id: 2, url: "https://www.url2.dev" },6 { id: 3, url: "https://www.link3.dev" }7 ];8 const output = [{ id: 3, url: "https://www.link3.dev" }];9 expect(filterByTerm(input, "link")).toEqual(output);10 });11 });

    (有关 Jest 匹配器的更多信息,请查看文档(https://jestjs.io/docs/en/getting-started))。
    你可以这样执行测试:

    1 npm test

    你会看到测试失败了:

    1 FAIL __tests__/filterByTerm.spec.js2 Filter function3 ✕ it should filter by a search term (2ms)4 ● Filter function › it should filter by a search term (link)5 ReferenceError: filterByTerm is not defined6 9 | const output = [{ id: 3, url: "https://www.link3.dev" }];7 10 |8 > 11 | expect(filterByTerm(input, "link")).toEqual(output);9 | ^10 12 | });11 13 | });12 14 |

    “ReferenceError: filterByTerm is not defined”. 实际上这是一件好事。我们会在下一节修复它!

    修复测试

    真正缺少的是 filterByTerm 的实现。为方便起见,我们将在测试所在的同一文件中创建该函
    数。在一个实际项目中,你需要在另一个文件中定义该函数并从测试文件中导入它。
    为了进行测试,我们将使用一个名为 filter 的原生 JavaScript 函数,它可以过滤掉数组中的元
    素。这是 filterByTerm 的最小实现:

    1 function filterByTerm(inputArr, searchTerm) {2 return inputArr.filter(function(arrayElement) {2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 6/142 return inputArr.filter(function(arrayElement) {3 return arrayElement.url.match(searchTerm);4 });5 }

    以下是它的工作原理:对于输入数组的每个元素,我们检查“url”属性,使用 match 方法将其与正
    则表达式进行匹配。这是完整的代码:

    1 function filterByTerm(inputArr, searchTerm) {2 return inputArr.filter(function(arrayElement) {3 return arrayElement.url.match(searchTerm);4 });5 }6 describe("Filter function", () => {7 test("it should filter by a search term (link)", () => {8 const input = [9 { id: 1, url: "https://www.url1.dev" },10 { id: 2, url: "https://www.url2.dev" },11 { id: 3, url: "https://www.link3.dev" }12 ];13 const output = [{ id: 3, url: "https://www.link3.dev" }];14 expect(filterByTerm(input, "link")).toEqual(output);15 });16 });

    再次运行测试:

    1 npm test

    看到它通过了!

    1 PASS __tests__/filterByTerm.spec.js2 Filter function3 ✓ it should filter by a search term (link) (4ms)4 Test Suites: 1 passed, 1 total5 Tests: 1 passed, 1 total6 Snapshots: 0 total7 Time: 0.836s, estimated 1s

    很好。但我们完成了测试吗?还没有。使我们的函数失败需要什么条件?让我们用大写搜索词强
    调函数:

    1 function filterByTerm(inputArr, searchTerm) {2 return inputArr.filter(function(arrayElement) {3 return arrayElement.url.match(searchTerm);4 });5 }6 describe("Filter function", () => {7 test("it should filter by a search term (link)", () => {8 const input = [9 { id: 1, url: "https://www.url1.dev" },10 { id: 2, url: "https://www.url2.dev" },11 { id: 3, url: "https://www.link3.dev" }12 ];13 const output = [{ id: 3, url: "https://www.link3.dev" }];14 expect(filterByTerm(input, "link")).toEqual(output);15 expect(filterByTerm(input, "LINK")).toEqual(output); // New test2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 7/1416 });17 });

    运行测试……它将失败。该再次修复它了!

    Jest Tutorial: fixing the test for uppercase

    Jest Tutorial:修复大写测试

    filterByTerm 也应该考虑大写的搜索术语。换句话说,即使搜索项是大写字符串,它也应该返回
    匹配的对象:

    1 filterByTerm(inputArr, "link");2 filterByTerm(inputArr, "LINK");

    为了测试这种情况,我们引入了一个新测试:

    1 expect(filterByTerm(input, "LINK")).toEqual(output); // New test

    为了使它通过,我们可以调整提供给 match 的正则表达式:

    1 //2 return arrayElement.url.match(searchTerm);3 //

    我们可以构建一个不区分大小写的正则表达式,而不是直接传递 searchTerm,也就是说,无论
    怎样的字符串都匹配的表达式。这是修复:

    1 function filterByTerm(inputArr, searchTerm) {2 const regex = new RegExp(searchTerm, "i");3 return inputArr.filter(function(arrayElement) {4 return arrayElement.url.match(regex);5 });6 }

    这是完整的测试:

    1 describe("Filter function", () => {2 test("it should filter by a search term (link)", () => {3 const input = [4 { id: 1, url: "https://www.url1.dev" },5 { id: 2, url: "https://www.url2.dev" },6 { id: 3, url: "https://www.link3.dev" }7 ];8 const output = [{ id: 3, url: "https://www.link3.dev" }];9 expect(filterByTerm(input, "link")).toEqual(output);

2021/1/17 用 Jest 进行 JavaScript 测试[每日前端夜话0xB6]
https://mp.weixin.qq.com/s/r_vYk1HR7N-1XOc82SnnQQ 8/14
p ( y ( p , )) q ( p );
10 expect(filterByTerm(input, "LINK")).toEqual(output);
11 });
12 });
13 function filterByTerm(inputArr, searchTerm) {
14 const regex = new RegExp(searchTerm, "i");
15 return inputArr.filter(function(arrayElement) {
16 return arrayElement.url.match(regex);
17 });
18 }

再次运行并看到它通过。做得好!作为**练习,你要写两个新的测试**并检查以下条件:1. 测试搜索词“uRl”2. 测试**空搜索词**。该函数应如何处理?你将如何构建这些新测试?在下一节中,我们将看到测试的另一个重要主题:**代码覆盖率**。## 代码覆盖率什么是**代码覆盖率**?在谈论它之前,先让我们快速调整一下代码。在项目根目录中创建一个名为**src **的新文件夹,并创建一个名为 f**ilterByTerm.js** 的文件,放置并导出我们的函数:

1 mkdir src && cd _$
2 touch filterByTerm.js

这是文件 filterByTerm.js:

1 function filterByTerm(inputArr, searchTerm) {
2 if (!searchTerm) throw Error("searchTerm cannot be empty");
3 const regex = new RegExp(searchTerm, "i");
4 return inputArr.filter(function(arrayElement) {
5 return arrayElement.url.match(regex);
6 });
7 }
8 module.exports = filterByTerm;

现在假装我是你的新同事。我对测试一无所知,我应该直接在该函数内部添加一个新的 **if语句**,而不是要求更多的上下文:

1 function filterByTerm(inputArr, searchTerm) {
2 if (!searchTerm) throw Error("searchTerm cannot be empty");
3 if (!inputArr.length) throw Error("inputArr cannot be empty"); // new line
4 const regex = new RegExp(searchTerm, "i");
5 return inputArr.filter(function(arrayElement) {
6 return arrayElement.url.match(regex);
7 });
8 }
9 module.exports = filterByTerm;

filterByTerm 中有一行新的代码,似乎不会被测试。除非我告诉你“有一个新的测试声明”你不会**在我们的函数中确切地知道测试**。几乎不可能想象我们的代码可以采用的所有路径,因此**需要一种有助于揭示这些盲点的工具**。该工具被称为代码覆盖,它是工具箱中的强大工具。**Jest 具有内置代码覆盖率**,你可以通过两种方式激活:1.通过命令行传递标志“-coverage”2.通过在 package.json 中配置 Jest在使用 coverage 运行测试之前,请确保在 **tests/filterByTerm.spec.js **中导入 **filterByTerm**:

1const filterByTerm = require("../src/filterByTerm");
2// ...

保存文件并用 coverage 运行测试:

1npm test -- --coverage

这是你得到的结果:

1 PASS tests/filterByTerm.spec.js
2 Filter function
3 ✓ it should filter by a search term (link) (3ms)
4 ✓ it should filter by a search term (uRl) (1ms)
5 ✓ it should throw when searchTerm is empty string (2ms)
6-----------------|----------|----------|----------|----------|-------------------|
7File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
8-----------------|----------|----------|----------|----------|-------------------|
9All files | 87.5 | 75 | 100 | 100 | |
10 filterByTerm.js | 87.5 | 75 | 100 | 100 | 3 |
11-----------------|----------|----------|----------|----------|-------------------|
12Test Suites: 1 passed, 1 total
13Tests: 3 passed, 3 total

这是我们的功能测试范围的一个很好的总结。如你所见**第3行被uncovered**。尝试通过测试我添加的新语句来达到100%的代码覆盖率。如果你想保持代码覆盖率始终处于活动状态,请在 package.json 中配置Jest,如下所示:

1 "scripts": {
2 "test": "jest"
3 },
4 "jest": {
5 "collectCoverage": true
6 },

你还可以将标志传递给测试脚本:

1 "scripts": {
2 "test": "jest --coverage"
3 },

还有一种方法可以获得**代码覆盖率的HTML报告的方法**,它就像配置Jest一样:

1 "scripts": {
2 "test": "jest"
3 },
4 "jest": {
5 "collectCoverage": true,
6 "coverageReporters": ["html"]
7 },

现在,每次运行 npm test 时,你都可以在项目文件夹中访问名为 **coverage** 的新文件夹:**getting-started-with-jest/coverage/**。在该文件夹中,你将找到一堆文件,其中 **/coverage/index.html** 是代码覆盖范围的完整HTML摘要。![](https://s4.51cto.com/images/blog/202101/17/5066da62346542d329de849766536b37.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)Jest的HTML代码覆盖率报告如果单击函数名称,你还会看到确切的未经测试的代码行:![](https://s4.51cto.com/images/blog/202101/17/0926ef4c8620ea0fceceb4b1a691bfca.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)单个文件的Jest代码覆盖率报告很整洁不是吗?使用**代码覆盖,你可以在有疑问时发现要测试的内容**。## 如何测试 React?**React **是一个非常流行的 JavaScript 库,用于创建动态用户界面。Jest 可以顺利地测试 React 应用(Jest 和 React 均来自 Facebook 的工程师)。Jest 也是 Create React App 中的默认测试器。如果你想学习如何测试React组件,请查看**测试React组件:最明确的指南**【https://www.valentinog.com/blog/testing-react/】。该指南涵盖了**单元测试组件**、类组件、**带hook的功能组件**和新的 Act API。## 结论(从这里开始)**测试是一个很大而且引人入胜的话题**。有许多类型的测试和用于测试的库。在这个 Jest 教程中,你学习了如何为覆盖率报告配置 Jest,如何组织和编写简单的**单元测试**,以及如何测试 JavaScript 代码。要了解有关** UI测试**的更多信息,我强烈建议你查看用 Cypress 进行 JavaScript 端到端测试【https://www.valentinog.com/blog/cypress/】。即使它与 JavaScript 无关,我也建议阅读 Harry Percival 的使用 Python 进行测试驱动开发【https://www.obeythetestinggoat.com/】。它包含了所有测试内容的提示和技巧,并深入介绍了所有不同类型的测试。如果你已准备好再迈出一步,要了解**自动化测试和持续集成**那么JavaScript中的自动化测试和持续集成【https://www.valentinog.com/blog/ci-js/】是为你准备的。你可以在Github上找到本教程的代码:getting-started-with-jest以及练习的解决方案【https://github.com/valentinogagliardi/getting-started-with-jest】。原文:https://www.valentinog.com/blog/jest/
©著作权归作者所有:来自51CTO博客作者mb5ff980b461ced的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. Javascript 性能测试 [每日前端夜话0xB1]
  2. 用 await/async 正确链接 Javascript 中的多个函数[每日前端夜话
  3. 用原生 JavaScript 实现十大 jQuery 函数[每日前端夜话0x94]
  4. pytest测试入门篇(ExitCode退出码)
  5. 性能测试 —— MySQL 基准测试
  6. 性能测试 —— Redis 基准测试
  7. 性能测试 —— Dubbo 基准测试
  8. shell脚本之灵活调用函数技巧
  9. Azure DevTest Lab体验(二)用户测试

随机推荐

  1. 让TextView 自带滚动条
  2. Android:TextView 自动滚动(跑马灯)
  3. React Navigation Android(安卓)返回键事
  4. Android中常用的XML生成方法实例分析
  5. adb 全部命令
  6. (二)Android系统信息
  7. Android 自定义View(手写签名)
  8. Android平台开发-Android keypad map-And
  9. DDM dispatch reg wait timeout , Can't
  10. Android实现随屏幕旋转的TextView