标题: http://zetcode.com/javascript/jest/

Jest 教程展示了如何使用 Jest 框架在 JavaScript 应用中执行单元测试。

Jest

Jest JavaScript REST 框架,其重点是简单性。 Jest 是由 Facebook 工程师为其 React 项目创建的。

单元测试是一种软件测试,其中测试软件的各个单元(组件)。 单元测试的目的是验证软件的每个单元是否按设计执行。 单元是所有软件中最小的可测试部分。

模拟是一种技术,其中代码部分被模拟真实代码的虚拟实现所替代。 模拟有助于实现测试隔离。 模拟主要用于单元测试。

在我们的测试中,我们检查值是否满足某些条件。 expect()函数为我们提供了许多匹配器,这些匹配器使我们可以验证不同的事物,例如toBe(),toBeFalsy()或toEqual()。

在本教程中,我们在 Node 应用中使用 Jest。

安装 Jest

首先,我们安装 Jest。

  1. $ node -v
  2. v11.5.0

我们使用 Node 版本 11.5.0。

  1. $ npm init -y

我们启动一个新的 Node 应用。

  1. $ npm i --dev jest

我们用nmp i --dev jest安装 Jest 模块。

  1. $ npm i -g jsonserver
  2. $ npm i axios

我们也将使用jsonserver和axios。

package.json

测试脚本运行jest。

package.json

  1. {
  2. "name": "jest-test",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "main.js",
  6. "scripts": {
  7. "test": "jest --verbose"
  8. },
  9. "keywords": [],
  10. "author": "Jan Bodnar",
  11. "license": "ISC",
  12. "devDependencies": {
  13. "jest": "^24.0.0"
  14. },
  15. "dependencies": {
  16. "axios": "^0.18.0"
  17. }
  18. }

缺省情况下,笑话仅提供基本输出。 要获得有关测试运行的更多信息,我们使用--verbose标志。

Jest 运行测试

使用npm test命令运行测试。 测试文件的名称中必须带有test术语。

  1. $ npm test
  2. > jest-test@1.0.0 test C:\Users\Jano\Documents\js\jest-test
  3. > jest
  4. PASS ./math-utils.test.js
  5. PASS ./arith.test.js
  6. PASS ./arith-params.test.js
  7. PASS ./arith-skip.test.js
  8. PASS ./string-utils.test.js
  9. PASS ./arith-mock.test.js
  10. PASS ./users.test.js
  11. Test Suites: 7 passed, 7 total
  12. Tests: 2 skipped, 35 passed, 37 total
  13. Snapshots: 0 total
  14. Time: 5.19s
  15. Ran all test suites.

这是使用 Jest 运行测试的示例输出。 这是一个简洁的输出。 有关更多信息,我们可以使用--verbose选项。

要运行单个测试,我们可以使用npx jest testname命令。

  1. scripts:{
  2. "test": "jest --verbose ./test-directory"
  3. }

我们可以配置 Jest 在指定的测试目录中运行测试。

用 Jest 测试算术函数

以下是使用 Jest 降级单元测试的经典学术示例。

arith.js

  1. const add = (a, b) => a + b;
  2. const mul = (a, b) => a * b;
  3. const sub = (a, b) => a - b;
  4. const div = (a, b) => a / b;
  5. module.exports = { add, mul, sub, div };

一个模块中有四个基本算术函数。

arith.test.js

  1. const { add, mul, sub, div } = require('./arith');
  2. test('2 + 3 = 5', () => {
  3. expect(add(2, 3)).toBe(5);
  4. });
  5. test('3 * 4 = 12', () => {
  6. expect(mul(3, 4)).toBe(12);
  7. });
  8. test('5 - 6 = -1', () => {
  9. expect(sub(5, 6)).toBe(-1);
  10. });
  11. test('8 / 4 = 2', () => {
  12. expect(div(8, 4)).toBe(2);
  13. });

在arith.test.js中,我们测试模块。 文件名包含测试词。 然后由玩笑挑出来。

  1. test('2 + 3 = 5', () => {
  2. expect(add(2, 3)).toBe(5);
  3. });

我们使用test()函数测试add()方法。 第一个参数是测试的名称,第二个参数是要运行的函数。 我们正在测试add()函数返回的样本数据正确答案。

  1. $ npx jest arith.test.js
  2. PASS ./arith.test.js
  3. √ 2 + 3 = 5 (3ms)
  4. √ 3 * 4 = 12 (6ms)
  5. √ 5 - 6 = -1
  6. √ 8 / 4 = 2 (1ms)
  7. Test Suites: 1 passed, 1 total
  8. Tests: 4 passed, 4 total
  9. Snapshots: 0 total
  10. Time: 10.981s
  11. Ran all test suites matching /arith.test.js/i.

这是输出。

Jest 跳过测试

测试可能需要花费大量时间才能完成。 如果需要,我们可以跳过一些测试。

arith-skip.test.js

  1. const { add, mul, sub, div } = require('./arith');
  2. xtest('2 + 3 = 5', () => {
  3. expect(add(2, 3)).toBe(5);
  4. });
  5. test.skip('3 * 4 = 12', () => {
  6. expect(mul(3, 4)).toBe(12);
  7. });
  8. test('5 - 6 = -1', () => {
  9. expect(sub(5, 6)).toBe(-1);
  10. });
  11. test('8 / 4 = 2', () => {
  12. expect(div(8, 4)).toBe(2);
  13. });

可以使用skip()或使用x前缀跳过测试。 在我们的例子中,前两个测试被跳过。

  1. $ npx jest arith-skip.test.js
  2. PASS ./arith-skip.test.js
  3. √ 5 - 6 = -1 (2ms)
  4. √ 8 / 4 = 2 (1ms)
  5. ○ skipped 2 tests
  6. Test Suites: 1 passed, 1 total
  7. Tests: 2 skipped, 2 passed, 4 total
  8. Snapshots: 0 total
  9. Time: 2.323s
  10. Ran all test suites matching /arith-skip.test.js/i.

跳过了两个测试。

Jest 参数化测试

参数化测试允许我们使用不同的值多次运行相同的测试。 这使我们的测试功能更强大。

对于参数化测试,我们使用each()全局函数。

arith-param.test.js

  1. const { add, mul, sub, div } = require('./arith')
  2. test.each([[1, 1, 2], [-1, 2, 1], [2, 1, 3]])(
  3. '%i + %i equals %i', (a, b, expected) => {
  4. expect(add(a, b)).toBe(expected);
  5. },
  6. );
  7. test.each([[1, 1, 0], [-1, 2, -3], [2, 2, 0]])(
  8. '%i - %i equals %i', (a, b, expected) => {
  9. expect(sub(a, b)).toBe(expected);
  10. },
  11. );
  12. test.each([[1, 1, 1], [-1, 2, -2], [2, 2, 4]])(
  13. '%i * %i equals %i', (a, b, expected) => {
  14. expect(mul(a, b)).toBe(expected);
  15. },
  16. );
  17. test.each([[1, 1, 1], [-1, 2, -0.5], [2, 2, 1]])(
  18. '%i / %i equals %i', (a, b, expected) => {
  19. expect(div(a, b)).toBe(expected);
  20. },
  21. );

在这些测试中,我们使用不同的输入数据多次运行每个算术函数。

  1. test.each([[1, 1, 2], [-1, 2, 1], [2, 1, 3]])(
  2. '%i + %i equals %i', (a, b, expected) => {
  3. expect(add(a, b)).toBe(expected);
  4. },
  5. );

each()方法接收一个数组数组,该数组的参数将传递给每一行的测试函数。 %i是需要整数的格式说明符。 这是针对--verbose选项显示的输出的。

  1. $ npx jest arith-params.test.js
  2. PASS ./arith-params.test.js
  3. √ 1 + 1 equals 2 (3ms)
  4. √ -1 + 2 equals 1 (1ms)
  5. √ 2 + 1 equals 3
  6. √ 1 - 1 equals 0
  7. √ -1 - 2 equals -3
  8. √ 2 - 2 equals 0
  9. √ 1 * 1 equals 1 (1ms)
  10. √ -1 * 2 equals -2
  11. √ 2 * 2 equals 4
  12. √ 1 / 1 equals 1 (1ms)
  13. √ -1 / 2 equals 0
  14. √ 2 / 2 equals 1
  15. Test Suites: 1 passed, 1 total
  16. Tests: 12 passed, 12 total
  17. Snapshots: 0 total
  18. Time: 1.759s
  19. Ran all test suites matching /arith-params.test.js/i.

这是输出。

Jest

beforeAll()函数是测试设置的一部分。 它会在运行此文件中的任何测试之前运行一个函数。 如果函数返回一个Promise或是一个生成器,则 Jest 在运行测试之前等待该Promise解析。

math-utils.js

  1. const sum = (vals) => {
  2. let sum = 0;
  3. vals.forEach((val) => {
  4. sum += val;
  5. });
  6. return sum;
  7. }
  8. const positive = (vals) => {
  9. return vals.filter((x) => { return x > 0; });
  10. }
  11. const negative = (vals) => {
  12. return vals.filter((x) => { return x < 0; });
  13. }
  14. module.exports = { sum, positive, negative };

我们有一个math-utils模块,其中包含三个函数:sum(),positive()和negative()。

math-utils.test.js

  1. const { sum, positive, negative } = require('./math-utils');
  2. let vals;
  3. let sum_of_vals;
  4. let pos_vals;
  5. let neg_vals;
  6. beforeAll(() => {
  7. pos_vals = [2, 1, 3];
  8. neg_vals = [-2, -1, -1];
  9. vals = pos_vals.concat(neg_vals);
  10. sum_of_vals = vals.reduce((x, y) => x + y, 0);
  11. })
  12. test('the sum of vals should be 2', () => {
  13. expect(sum(vals)).toBe(sum_of_vals);
  14. });
  15. test('should get positive values', () => {
  16. expect(positive(vals)).toEqual(pos_vals);
  17. });
  18. test('should get negative values', () => {
  19. expect(negative(vals)).toEqual(neg_vals);
  20. });

在测试文件中,我们使用beforeAll()函数的测试运行之前初始化测试数据。

  1. test('should get positive values', () => {
  2. expect(positive(vals)).toEqual(pos_vals);
  3. });

为了测试positive()函数,我们使用toEqual()匹配器。 我们测试该函数返回的正值数组等于预定义的测试值数组。

Jest 分组测试

在 Jest 中,测试使用describe()分组为单位。 它创建一个将几个相关测试组合在一起的模块。

string-utils.js

  1. const isPalindrome = (string) => string == string.split('').reverse().join('');
  2. const isAnagram = (w1, w2) => {
  3. const regularize = (word) => {
  4. return word.toLowerCase().split('').sort().join('').trim();
  5. }
  6. return regularize(w1) === regularize(w2);
  7. }
  8. module.exports = {isPalindrome, isAnagram};

我们的string-utils.js模块具有两个函数:isPalindrome()和isAnagram()。

math-utils.js

  1. const sum = (vals) => {
  2. let sum = 0;
  3. vals.forEach((val) => {
  4. sum += val;
  5. });
  6. return sum;
  7. }
  8. const positive = (vals) => {
  9. return vals.filter((x) => { return x > 0; });
  10. }
  11. const negative = (vals) => {
  12. return vals.filter((x) => { return x < 0; });
  13. }
  14. module.exports = { sum, positive, negative };

我们又有了math-utils.js模块。

groups.test.js

  1. const { sum, positive, negative } = require('./math-utils');
  2. const { isPalindrome, isAnagram } = require('./string-utils');
  3. describe('testing math utilities', () => {
  4. let vals;
  5. let sum_of_vals;
  6. let pos_vals;
  7. let neg_vals;
  8. beforeAll(() => {
  9. pos_vals = [2, 1, 3];
  10. neg_vals = [-2, -1, -1];
  11. vals = pos_vals.concat(neg_vals);
  12. sum_of_vals = vals.reduce((x, y) => x + y, 0);
  13. })
  14. test('the sum of vals should be 2', () => {
  15. expect(sum(vals)).toBe(sum_of_vals);
  16. });
  17. test('should get positive values', () => {
  18. expect(positive(vals)).toEqual(pos_vals);
  19. });
  20. test('should get negative values', () => {
  21. expect(negative(vals)).toEqual(neg_vals);
  22. });
  23. });
  24. describe('testing string utilities', () => {
  25. test.each(["racecar", "radar", "level", "refer", "deified", "civic"])(
  26. 'testing %s for palindrome', (word) => {
  27. expect(isPalindrome(word)).toBeTruthy();
  28. },
  29. );
  30. test.each([["arc", "car"], ["cat", "act"], ["cider", "cried"]])(
  31. 'testing if %s and %s are anagrams ', (word1, word2) => {
  32. expect(isAnagram(word1, word2)).toBeTruthy();
  33. },
  34. );
  35. });

使用describe(),我们为字符串和数学工具创建了两个独立的测试组。 例如,beforeAll()仅适用于数学工具。

  1. $ npx jest groups.test.js
  2. PASS ./groups.test.js
  3. testing math utilities
  4. √ the sum of vals should be 2 (3ms)
  5. √ should get positive values (1ms)
  6. √ should get negative values
  7. testing string utilities
  8. √ testing racecar for palindrome (1ms)
  9. √ testing radar for palindrome
  10. √ testing level for palindrome
  11. √ testing refer for palindrome
  12. √ testing deified for palindrome (1ms)
  13. √ testing civic for palindrome
  14. √ testing if arc and car are anagrams
  15. √ testing if cat and act are anagrams
  16. √ testing if cider and cried are anagrams (1ms)
  17. Test Suites: 1 passed, 1 total
  18. Tests: 12 passed, 12 total
  19. Snapshots: 0 total
  20. Time: 1.786s
  21. Ran all test suites matching /groups.test.js/i.

我们运行测试。

Jest 测试 Axios

在以下部分中,我们将测试使用 Axios 库的 JavaScript 代码。 首先,我们安装了axios和json-server模块。

users.json

  1. {
  2. "users": [
  3. {
  4. "id": 1,
  5. "first_name": "Robert",
  6. "last_name": "Schwartz",
  7. "email": "rob23@gmail.com"
  8. },
  9. {
  10. "id": 2,
  11. "first_name": "Lucy",
  12. "last_name": "Ballmer",
  13. "email": "lucyb56@gmail.com"
  14. },
  15. {
  16. "id": 3,
  17. "first_name": "Anna",
  18. "last_name": "Smith",
  19. "email": "annasmith23@gmail.com"
  20. },
  21. {
  22. "id": 4,
  23. "first_name": "Robert",
  24. "last_name": "Brown",
  25. "email": "bobbrown432@yahoo.com"
  26. },
  27. {
  28. "id": 5,
  29. "first_name": "Roger",
  30. "last_name": "Bacon",
  31. "email": "rogerbacon12@yahoo.com"
  32. }
  33. ]
  34. }

这是 JSON 服务器的一些伪造数据。

users.js

  1. const axios = require('axios');
  2. class Users {
  3. static async all() {
  4. let res = await axios.get('http://localhost:3000/users');
  5. return res;
  6. }
  7. }
  8. module.exports = Users;

users.js模块使用axios检索数据。 我们将测试该模块。

users-app.js

  1. const Users = require('./users');
  2. async function showData() {
  3. let res = await Users.all();
  4. console.log(res.data);
  5. }
  6. showData();
  7. console.log('finished')

users-app.js是使用users.js模块获取和输出数据的应用。

  1. $ json-server --watch users.json

我们开始json-server。

  1. $ node users-app.js
  2. finished
  3. [ { id: 1,
  4. first_name: 'Robert',
  5. last_name: 'Schwartz',
  6. email: 'rob23@gmail.com' },
  7. { id: 2,
  8. first_name: 'Lucy',
  9. last_name: 'Ballmer',
  10. email: 'lucyb56@gmail.com' },
  11. { id: 3,
  12. first_name: 'Anna',
  13. last_name: 'Smith',
  14. email: 'annasmith23@gmail.com' },
  15. { id: 4,
  16. first_name: 'Robert',
  17. last_name: 'Brown',
  18. email: 'bobbrown432@yahoo.com' },
  19. { id: 5,
  20. first_name: 'Roger',
  21. last_name: 'Bacon',
  22. email: 'rogerbacon12@yahoo.com' } ]

我们运行该应用。

users.test.js

  1. const axios = require('axios');
  2. const Users = require('./users');
  3. jest.mock('axios');
  4. test('should fetch users', () => {
  5. const users = [{
  6. "id": 1,
  7. "first_name": "Robert",
  8. "last_name": "Schwartz",
  9. "email": "rob23@gmail.com"
  10. }, {
  11. "id": 2,
  12. "first_name": "Lucy",
  13. "last_name": "Ballmer",
  14. "email": "lucyb56@gmail.com"
  15. }];
  16. const resp = { data : users };
  17. axios.get.mockImplementation(() => Promise.resolve(resp));
  18. Users.all().then(resp => expect(resp.data).toEqual(users));
  19. });

该测试文件测试users.js模块。

  1. jest.mock('axios');

我们模拟模块。

  1. const users = [{
  2. "id": 1,
  3. "first_name": "Robert",
  4. "last_name": "Schwartz",
  5. "email": "rob23@gmail.com"
  6. }, {
  7. "id": 2,
  8. "first_name": "Lucy",
  9. "last_name": "Ballmer",
  10. "email": "lucyb56@gmail.com"
  11. }];
  12. const resp = { data : users };

这是模拟模块将返回的响应。

  1. axios.get.mockImplementation(() => Promise.resolve(resp));

模拟实现返回带有响应的promise。

  1. Users.all().then(resp => expect(resp.data).toEqual(users));

我们测试了模拟的Users.all()函数。

  1. $ npx jest users.test.js
  2. PASS ./users.test.js
  3. √ should fetch users (4ms)
  4. Test Suites: 1 passed, 1 total
  5. Tests: 1 passed, 1 total
  6. Snapshots: 0 total
  7. Time: 1.818s
  8. Ran all test suites matching /users.test.js/i.

我们进行测试。

在本教程中,我们使用 Jest 在 JavaScript 应用中进行单元测试。

您可能也对以下相关教程感兴趣: Moment.js 教程, Axios 教程, Faker.js 教程, JSONServer 教程 ,从 JavaScript 中的 URL 读取 JSON , Node Sass 教程, Lodash 教程。