当你建立一个应用程序时,手动测试是非常容易的事情。从你开始写第一行代码起,你就一直在这样做了!为什么?怎么做?想一想吧。你写一些代码,保存它,然后运行代码,看看结果是什么。它给出了你想要的结果吗?很好!那么它就成功了。那么,如果测试你写的代码是如此简单,为什么还要写自动测试呢?原因是,随着你的应用程序越来越大,手动测试应用程序的每一部分需要越来越多的时间。

测试的好处是什么?
自动测试允许你在少量的时间内频繁地测试你的代码。这意味着你可以在实际推送代码到生产之前发现问题和错误。如果你曾经第一次部署一个应用程序,你知道随之而来的担忧。它能工作吗?是否有bug?自动测试有助于给你更多的信心,让你知道事情在需要的时候会工作。公平地说,一旦软件进入生产阶段,测试并不能使你免于处理问题和bug。无论你写多少测试,都不可能写出完美的软件。然而,与其部署一个有25个bug的应用程序,不如部署一个只有5个bug的应用程序,这要感谢你所有的测试。另一个好处是允许你更有信心地重构你的代码。换句话说,你可以清理你的功能,或者改变代码本身的结构,使其更易读,同时还可以让代码执行相同的确切任务。
软件开发中的测试类型有哪些?

一般来说,在软件开发中,有三种类型的自动化测试。
- **单元测试。**测试应用程序的一个小单元,没有外部资源,如数据库。
- 集成测试。在所有外部资源到位的情况下测试应用程序。
- 功能/端到端测试。通过其用户界面测试应用程序。
测试的金字塔
在建立一个应用程序时,应该进行哪些类型的测试?如果我们有几种类型,哪种是最好的?最有可能的是,你会有三种类型的测试的组合。最大量的测试是单元测试,因为它们很容易编写和快速执行。接下来,你会有一个集成测试的集合。应用程序中的集成测试可能比单元测试少。最后,你会有一些端到端的测试,因为这些是最全面的,但也是最慢的。这种方法看起来像一个金字塔。注意E2E代表 "端到端 "或功能。

这意味着你会把大部分的测试写成单元测试。对于测试覆盖率中出现的任何差距,使用集成测试。最后,最少使用端到端测试。
编写你的第一个单元测试
那么,我们什么时候才能写一个测试呢!我们现在就开始吧!首先,我们要创建一个javascript-testing目录,然后我们可以用NPM来安装Jest。让我们首先使用npm init来创建一个新的package.json文件。
javascript-testing $npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.Press ^C at any time to quit.
package name: (javascript-testing)
version: (1.0.0)
description: JavaScript Test Tutorial
entry point: (index.js)
test command: jest
git repository:
keywords:
author:
license: (ISC)
About to write to C:nodejavascript-testingpackage.json:
{
"name": "javascript-testing",
"version": "1.0.0",
"description": "JavaScript Test Tutorial",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes)
现在我们可以安装Jest了。

现在我们应该看一下package.json文件中突出显示的那一行。这是说,当我们在命令行中运行npm test ,jest 脚本将运行以执行所有测试。
{
"name": "javascript-testing",
"version": "1.0.0",
"description": "JavaScript Test Tutorial",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^23.4.1"
}
}
让我们试试,看看会发生什么。
javascript-testing $npm test
> javascript-testing@1.0.0 test C:nodejavascript-testing
> jest
No tests foundIn C:nodejavascript-testing
2 files checked. testMatch: **/__tests__/**/*.js?(x),**/?(*.)+(spec|test).js?(x) - 0 matches testPathIgnorePatterns: \node_modules\ - 2 matches
Pattern: - 0 matches
npm ERR! Test failed. See above for more details.
javascript-testing $
很好!Jest正在工作,但我们还没有任何测试。让我们创建一个!在我们的项目中,我们可以添加一个tests 文件夹。在这个文件夹中,我们将放置一个utility.test.js 文件。

事实上,文件名中出现的test这个词将让Jest知道这是一个测试。这里是我们的第一个测试。它还没有测试任何东西,但这将允许我们在命令行中运行npm test ,看看会发生什么。
test('First Jest Test', () => {
});
现在,我们运行我们的测试,并检查一下!

上面我们看到Jest中通过的测试是什么样子的。现在,让我们看看一个失败的测试是什么样子的。我们可以像这样修改我们的测试。
test('First Jest Test', () => {
throw new Error('It crashed!');
});
现在,当我们运行它时,我们会得到一大堆有用的信息。

如何测试数字
在我们的utility.js文件中,有一个函数可以创建一个绝对数字。换句话说,它将永远不会返回一个负数。
module.exports.absolute = function (number) {
if (number > 0) return number;
if (number < 0) return -number;
return 0;
}
让我们写一个新的测试来测试这个。这里是第一个迭代。
const utility = require('../utility');
test('absolute - should return positive number for any positive input', () => {
const result = utility.absolute(1);
expect(result).toBe(1);
});
我们可以运行这个测试,它通过了。
javascript-testing $npm test
> javascript-testing@1.0.0 test C:nodejavascript-testing
> jest
PASS tests/utility.test.js
√ absolute - should return positive number for any positive input (6ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 6.351s, estimated 7s
Ran all test suites.
这个函数还应该返回一个正数,即使给定一个负数。没问题,我们可以把它添加到测试中。
const utility = require('../utility');
test('absolute - should return positive number for any positive input', () => {
const result = utility.absolute(1);
expect(result).toBe(1);
});
test('absolute - should return positive number for any negative input', () => {
const result = utility.absolute(-1);
expect(result).toBe(1);
});
现在当我们运行测试时,它又通过了。很好!还请注意我们是如何被显示出运行了多少个测试的。

现在让我们做一个失败的测试,以展示如何使用预期值与接收值来排除故障。我们知道,如果这个函数的输入值为0,它应该返回0。我们可以做一个测试,让它在这个条件下失败,像这样。
const utility = require('../utility');
test('absolute - should return positive number for any positive input', () => {
const result = utility.absolute(1);
expect(result).toBe(1);
});
test('absolute - should return positive number for any negative input', () => {
const result = utility.absolute(-1);
expect(result).toBe(1);
});
test('absolute - should return 0 if input is 0', () => {
const result = utility.absolute(-1);
expect(result).toBe(0);
});
看看我们从一个失败的测试中得到的输出。

这只是为了显示失败的测试是什么样子的。我们可以修复这个测试,现在这个1个函数的3个测试都成功了,看起来是这样的。

如何测试字符串
现在我们可以测试一个处理字符串的函数。考虑一下这个简单的函数,当某人提供他们的名字时,向他们问好。
module.exports.hello = function (name) {
return 'Hello ' + name + '!';
}
让我们为它做一个测试。这个测试将使用一个新的语法。
const utility = require('../utility');
describe('hello', () => {
it('should return the hello message', () => {
const result = utility.hello('Stranger');
expect(result).toBe('Hello Stranger!');
})
});
当我们运行它时,测试看起来不错。
javascript-testing $npm test
> javascript-testing@1.0.0 test C:nodejavascript-testing
> jest
PASS tests/utility.test.js
hello
√ should return the hello message (7ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 6.977s
Ran all test suites.
在测试字符串时,你可以使用的另一个选项是toContain()匹配器而不是toBe()。原因是测试可能过于具体,可能容易失败。下面是同一个测试的一个稍微灵活的版本。
const utility = require('../utility');
describe('hello', () => {
it('should return the hello message', () => {
const result = utility.hello('Stranger');
expect(result).toContain('Stranger');
})
});
如何测试数组
让我们来看看我们可以在Jest中使用的一些简单的数组测试。想象一下,我们有这样一个函数,它返回一个股票代码的数组。
module.exports.getTickers = function () {
return ['AAPL', 'MSFT', 'NFLX'];
}
下面是我们为此创建的测试。查阅文档以了解arrayContaining()函数是如何工作的。
const utility = require('../utility');
describe('getTickers', () => {
it('should return three stock tickers', () => {
const result = utility.getTickers();
expect(result).toEqual(expect.arrayContaining(['NFLX', 'MSFT', 'AAPL']));
})
});
如何测试对象
在这个函数中,我们传递了一个游戏的ID,该函数返回一个具有该ID的游戏对象。
module.exports.getGame = function (gameId) {
return {
id: gameId,
price: 10
};
}
这里有一个测试,可以确保我们得到正确的游戏。
const utility = require('../utility');
describe('getGame', () => {
it('should return the game with the provided id', () => {
const result = utility.getGame(1);
expect(result).toEqual({
id: 1,
price: 10
})
})
});
当我们运行这个测试时,一切看起来都很好。

如何测试异常
对于有能力抛出错误的函数,我们需要采取不同的方法来测试。考虑一下这个函数,它创建了一个新的用户。如果没有提供用户名,它将抛出一个异常。
module.exports.createUser = function (username) {
if (!username) throw new Error('Username is required.');
return {
id: new Date().getTime(),
username: username
}
}
下面是我们如何测试这样的一个函数。
const utility = require('../utility');
describe('createUser', () => {
it('should throw an error if username is falsy', () => {
const args = [null, undefined, NaN, '', 0, false];
args.forEach(a => {
expect(() => {
utility.createUser(a)
}).toThrow();
});
})
it('should return a user object when a valid username is provided', () => {
const result = utility.createUser('Jester');
expect(result).toMatchObject({
username: 'Jester'
});
expect(result.id).toBeGreaterThan(0);
});
});
当我们运行它时,测试是通过的。
javascript-testing $npm test
> javascript-testing@1.0.0 test C:nodejavascript-testing
> jest
PASS tests/utility.test.js
createUser
√ should throw an error if username is falsy (9ms)
√ should return a user object when a valid username is provided (4ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.693s
Ran all test suites.
用Jest测试JavaScript总结
综上所述,我们已经学到了以下一些关于测试的概念。
-
编写代码来测试应用程序的代码被称为自动化测试。
-
测试有助于以更少的错误和更高的质量交付软件。
-
测试有助于你自信地进行重构。
-
Jest是一个强大的测试框架,拥有测试JavaScript所需的一切。
-
有三种类型的自动化测试。
-
单元测试:测试应用程序的一个小单元,没有外部资源,如数据库。
-
集成测试:在所有外部资源到位的情况下测试应用程序。
-
功能/端到端测试:通过其用户界面测试应用程序。
-
如果测试过于笼统,就不能保证你的代码工作。如果他们太具体,他们往往太容易损坏。因此,测试的目标是既不过于笼统,也不过于具体。
-
你可以使用Mock函数来将应用程序代码与外部资源隔离。
-
你在测试中可能使用的Jest匹配器函数。
-
// 平等性
expect(...).toBe();
expect(...).toEqual()。 -
// 真实性
expect(...).toBeDefined();
expect(...).toBeNull();
expect(...).toBeTruthy();
expect(...).toBeFalsy() -
// 数字
expect(...).toBeGreaterThan();
expect(...).toBeGreaterThanOrEqual();
expect(...).toBeLessThan() -
// 字符串
expect(...).toMatch(/regularExp/); -
// 数组
expect(...).toContain()。 -
// 对象
expect(...).toBe(); // 用于对象引用
expect(...).toEqual(); // 用于属性相等
expect(...).toMatchObject()。 -
// 异常
expect(() => { someCode }).toThrow()。