Jest 教程展示了如何使用 Jest 框架在 JavaScript 应用中执行单元测试。
Jest
Jest JavaScript REST 框架,其重点是简单性。 Jest 是由 Facebook 工程师为其 React 项目创建的。
单元测试是一种软件测试,其中测试软件的各个单元(组件)。 单元测试的目的是验证软件的每个单元是否按设计执行。 单元是所有软件中最小的可测试部分。
模拟是一种技术,其中代码部分被模拟真实代码的虚拟实现所替代。 模拟有助于实现测试隔离。 模拟主要用于单元测试。
在我们的测试中,我们检查值是否满足某些条件。 expect()函数为我们提供了许多匹配器,这些匹配器使我们可以验证不同的事物,例如toBe(),toBeFalsy()或toEqual()。
在本教程中,我们在 Node 应用中使用 Jest。
安装 Jest
首先,我们安装 Jest。
$ node -vv11.5.0
我们使用 Node 版本 11.5.0。
$ npm init -y
我们启动一个新的 Node 应用。
$ npm i --dev jest
我们用nmp i --dev jest安装 Jest 模块。
$ npm i -g jsonserver$ npm i axios
我们也将使用jsonserver和axios。
package.json
测试脚本运行jest。
package.json
{"name": "jest-test","version": "1.0.0","description": "","main": "main.js","scripts": {"test": "jest --verbose"},"keywords": [],"author": "Jan Bodnar","license": "ISC","devDependencies": {"jest": "^24.0.0"},"dependencies": {"axios": "^0.18.0"}}
缺省情况下,笑话仅提供基本输出。 要获得有关测试运行的更多信息,我们使用--verbose标志。
Jest 运行测试
使用npm test命令运行测试。 测试文件的名称中必须带有test术语。
$ npm test> jest-test@1.0.0 test C:\Users\Jano\Documents\js\jest-test> jestPASS ./math-utils.test.jsPASS ./arith.test.jsPASS ./arith-params.test.jsPASS ./arith-skip.test.jsPASS ./string-utils.test.jsPASS ./arith-mock.test.jsPASS ./users.test.jsTest Suites: 7 passed, 7 totalTests: 2 skipped, 35 passed, 37 totalSnapshots: 0 totalTime: 5.19sRan all test suites.
这是使用 Jest 运行测试的示例输出。 这是一个简洁的输出。 有关更多信息,我们可以使用--verbose选项。
要运行单个测试,我们可以使用npx jest testname命令。
scripts:{"test": "jest --verbose ./test-directory"}
我们可以配置 Jest 在指定的测试目录中运行测试。
用 Jest 测试算术函数
以下是使用 Jest 降级单元测试的经典学术示例。
arith.js
const add = (a, b) => a + b;const mul = (a, b) => a * b;const sub = (a, b) => a - b;const div = (a, b) => a / b;module.exports = { add, mul, sub, div };
一个模块中有四个基本算术函数。
arith.test.js
const { add, mul, sub, div } = require('./arith');test('2 + 3 = 5', () => {expect(add(2, 3)).toBe(5);});test('3 * 4 = 12', () => {expect(mul(3, 4)).toBe(12);});test('5 - 6 = -1', () => {expect(sub(5, 6)).toBe(-1);});test('8 / 4 = 2', () => {expect(div(8, 4)).toBe(2);});
在arith.test.js中,我们测试模块。 文件名包含测试词。 然后由玩笑挑出来。
test('2 + 3 = 5', () => {expect(add(2, 3)).toBe(5);});
我们使用test()函数测试add()方法。 第一个参数是测试的名称,第二个参数是要运行的函数。 我们正在测试add()函数返回的样本数据正确答案。
$ npx jest arith.test.jsPASS ./arith.test.js√ 2 + 3 = 5 (3ms)√ 3 * 4 = 12 (6ms)√ 5 - 6 = -1√ 8 / 4 = 2 (1ms)Test Suites: 1 passed, 1 totalTests: 4 passed, 4 totalSnapshots: 0 totalTime: 10.981sRan all test suites matching /arith.test.js/i.
这是输出。
Jest 跳过测试
测试可能需要花费大量时间才能完成。 如果需要,我们可以跳过一些测试。
arith-skip.test.js
const { add, mul, sub, div } = require('./arith');xtest('2 + 3 = 5', () => {expect(add(2, 3)).toBe(5);});test.skip('3 * 4 = 12', () => {expect(mul(3, 4)).toBe(12);});test('5 - 6 = -1', () => {expect(sub(5, 6)).toBe(-1);});test('8 / 4 = 2', () => {expect(div(8, 4)).toBe(2);});
可以使用skip()或使用x前缀跳过测试。 在我们的例子中,前两个测试被跳过。
$ npx jest arith-skip.test.jsPASS ./arith-skip.test.js√ 5 - 6 = -1 (2ms)√ 8 / 4 = 2 (1ms)○ skipped 2 testsTest Suites: 1 passed, 1 totalTests: 2 skipped, 2 passed, 4 totalSnapshots: 0 totalTime: 2.323sRan all test suites matching /arith-skip.test.js/i.
跳过了两个测试。
Jest 参数化测试
参数化测试允许我们使用不同的值多次运行相同的测试。 这使我们的测试功能更强大。
对于参数化测试,我们使用each()全局函数。
arith-param.test.js
const { add, mul, sub, div } = require('./arith')test.each([[1, 1, 2], [-1, 2, 1], [2, 1, 3]])('%i + %i equals %i', (a, b, expected) => {expect(add(a, b)).toBe(expected);},);test.each([[1, 1, 0], [-1, 2, -3], [2, 2, 0]])('%i - %i equals %i', (a, b, expected) => {expect(sub(a, b)).toBe(expected);},);test.each([[1, 1, 1], [-1, 2, -2], [2, 2, 4]])('%i * %i equals %i', (a, b, expected) => {expect(mul(a, b)).toBe(expected);},);test.each([[1, 1, 1], [-1, 2, -0.5], [2, 2, 1]])('%i / %i equals %i', (a, b, expected) => {expect(div(a, b)).toBe(expected);},);
在这些测试中,我们使用不同的输入数据多次运行每个算术函数。
test.each([[1, 1, 2], [-1, 2, 1], [2, 1, 3]])('%i + %i equals %i', (a, b, expected) => {expect(add(a, b)).toBe(expected);},);
each()方法接收一个数组数组,该数组的参数将传递给每一行的测试函数。 %i是需要整数的格式说明符。 这是针对--verbose选项显示的输出的。
$ npx jest arith-params.test.jsPASS ./arith-params.test.js√ 1 + 1 equals 2 (3ms)√ -1 + 2 equals 1 (1ms)√ 2 + 1 equals 3√ 1 - 1 equals 0√ -1 - 2 equals -3√ 2 - 2 equals 0√ 1 * 1 equals 1 (1ms)√ -1 * 2 equals -2√ 2 * 2 equals 4√ 1 / 1 equals 1 (1ms)√ -1 / 2 equals 0√ 2 / 2 equals 1Test Suites: 1 passed, 1 totalTests: 12 passed, 12 totalSnapshots: 0 totalTime: 1.759sRan all test suites matching /arith-params.test.js/i.
这是输出。
Jest
beforeAll()函数是测试设置的一部分。 它会在运行此文件中的任何测试之前运行一个函数。 如果函数返回一个Promise或是一个生成器,则 Jest 在运行测试之前等待该Promise解析。
math-utils.js
const sum = (vals) => {let sum = 0;vals.forEach((val) => {sum += val;});return sum;}const positive = (vals) => {return vals.filter((x) => { return x > 0; });}const negative = (vals) => {return vals.filter((x) => { return x < 0; });}module.exports = { sum, positive, negative };
我们有一个math-utils模块,其中包含三个函数:sum(),positive()和negative()。
math-utils.test.js
const { sum, positive, negative } = require('./math-utils');let vals;let sum_of_vals;let pos_vals;let neg_vals;beforeAll(() => {pos_vals = [2, 1, 3];neg_vals = [-2, -1, -1];vals = pos_vals.concat(neg_vals);sum_of_vals = vals.reduce((x, y) => x + y, 0);})test('the sum of vals should be 2', () => {expect(sum(vals)).toBe(sum_of_vals);});test('should get positive values', () => {expect(positive(vals)).toEqual(pos_vals);});test('should get negative values', () => {expect(negative(vals)).toEqual(neg_vals);});
在测试文件中,我们使用beforeAll()函数的测试运行之前初始化测试数据。
test('should get positive values', () => {expect(positive(vals)).toEqual(pos_vals);});
为了测试positive()函数,我们使用toEqual()匹配器。 我们测试该函数返回的正值数组等于预定义的测试值数组。
Jest 分组测试
在 Jest 中,测试使用describe()分组为单位。 它创建一个将几个相关测试组合在一起的模块。
string-utils.js
const isPalindrome = (string) => string == string.split('').reverse().join('');const isAnagram = (w1, w2) => {const regularize = (word) => {return word.toLowerCase().split('').sort().join('').trim();}return regularize(w1) === regularize(w2);}module.exports = {isPalindrome, isAnagram};
我们的string-utils.js模块具有两个函数:isPalindrome()和isAnagram()。
math-utils.js
const sum = (vals) => {let sum = 0;vals.forEach((val) => {sum += val;});return sum;}const positive = (vals) => {return vals.filter((x) => { return x > 0; });}const negative = (vals) => {return vals.filter((x) => { return x < 0; });}module.exports = { sum, positive, negative };
我们又有了math-utils.js模块。
groups.test.js
const { sum, positive, negative } = require('./math-utils');const { isPalindrome, isAnagram } = require('./string-utils');describe('testing math utilities', () => {let vals;let sum_of_vals;let pos_vals;let neg_vals;beforeAll(() => {pos_vals = [2, 1, 3];neg_vals = [-2, -1, -1];vals = pos_vals.concat(neg_vals);sum_of_vals = vals.reduce((x, y) => x + y, 0);})test('the sum of vals should be 2', () => {expect(sum(vals)).toBe(sum_of_vals);});test('should get positive values', () => {expect(positive(vals)).toEqual(pos_vals);});test('should get negative values', () => {expect(negative(vals)).toEqual(neg_vals);});});describe('testing string utilities', () => {test.each(["racecar", "radar", "level", "refer", "deified", "civic"])('testing %s for palindrome', (word) => {expect(isPalindrome(word)).toBeTruthy();},);test.each([["arc", "car"], ["cat", "act"], ["cider", "cried"]])('testing if %s and %s are anagrams ', (word1, word2) => {expect(isAnagram(word1, word2)).toBeTruthy();},);});
使用describe(),我们为字符串和数学工具创建了两个独立的测试组。 例如,beforeAll()仅适用于数学工具。
$ npx jest groups.test.jsPASS ./groups.test.jstesting math utilities√ the sum of vals should be 2 (3ms)√ should get positive values (1ms)√ should get negative valuestesting string utilities√ testing racecar for palindrome (1ms)√ testing radar for palindrome√ testing level for palindrome√ testing refer for palindrome√ testing deified for palindrome (1ms)√ testing civic for palindrome√ testing if arc and car are anagrams√ testing if cat and act are anagrams√ testing if cider and cried are anagrams (1ms)Test Suites: 1 passed, 1 totalTests: 12 passed, 12 totalSnapshots: 0 totalTime: 1.786sRan all test suites matching /groups.test.js/i.
我们运行测试。
Jest 测试 Axios
在以下部分中,我们将测试使用 Axios 库的 JavaScript 代码。 首先,我们安装了axios和json-server模块。
users.json
{"users": [{"id": 1,"first_name": "Robert","last_name": "Schwartz","email": "rob23@gmail.com"},{"id": 2,"first_name": "Lucy","last_name": "Ballmer","email": "lucyb56@gmail.com"},{"id": 3,"first_name": "Anna","last_name": "Smith","email": "annasmith23@gmail.com"},{"id": 4,"first_name": "Robert","last_name": "Brown","email": "bobbrown432@yahoo.com"},{"id": 5,"first_name": "Roger","last_name": "Bacon","email": "rogerbacon12@yahoo.com"}]}
这是 JSON 服务器的一些伪造数据。
users.js
const axios = require('axios');class Users {static async all() {let res = await axios.get('http://localhost:3000/users');return res;}}module.exports = Users;
users.js模块使用axios检索数据。 我们将测试该模块。
users-app.js
const Users = require('./users');async function showData() {let res = await Users.all();console.log(res.data);}showData();console.log('finished')
users-app.js是使用users.js模块获取和输出数据的应用。
$ json-server --watch users.json
我们开始json-server。
$ node users-app.jsfinished[ { id: 1,first_name: 'Robert',last_name: 'Schwartz',email: 'rob23@gmail.com' },{ id: 2,first_name: 'Lucy',last_name: 'Ballmer',email: 'lucyb56@gmail.com' },{ id: 3,first_name: 'Anna',last_name: 'Smith',email: 'annasmith23@gmail.com' },{ id: 4,first_name: 'Robert',last_name: 'Brown',email: 'bobbrown432@yahoo.com' },{ id: 5,first_name: 'Roger',last_name: 'Bacon',email: 'rogerbacon12@yahoo.com' } ]
我们运行该应用。
users.test.js
const axios = require('axios');const Users = require('./users');jest.mock('axios');test('should fetch users', () => {const users = [{"id": 1,"first_name": "Robert","last_name": "Schwartz","email": "rob23@gmail.com"}, {"id": 2,"first_name": "Lucy","last_name": "Ballmer","email": "lucyb56@gmail.com"}];const resp = { data : users };axios.get.mockImplementation(() => Promise.resolve(resp));Users.all().then(resp => expect(resp.data).toEqual(users));});
该测试文件测试users.js模块。
jest.mock('axios');
我们模拟模块。
const users = [{"id": 1,"first_name": "Robert","last_name": "Schwartz","email": "rob23@gmail.com"}, {"id": 2,"first_name": "Lucy","last_name": "Ballmer","email": "lucyb56@gmail.com"}];const resp = { data : users };
这是模拟模块将返回的响应。
axios.get.mockImplementation(() => Promise.resolve(resp));
模拟实现返回带有响应的promise。
Users.all().then(resp => expect(resp.data).toEqual(users));
我们测试了模拟的Users.all()函数。
$ npx jest users.test.jsPASS ./users.test.js√ should fetch users (4ms)Test Suites: 1 passed, 1 totalTests: 1 passed, 1 totalSnapshots: 0 totalTime: 1.818sRan all test suites matching /users.test.js/i.
我们进行测试。
在本教程中,我们使用 Jest 在 JavaScript 应用中进行单元测试。
您可能也对以下相关教程感兴趣: Moment.js 教程, Axios 教程, Faker.js 教程, JSONServer 教程 ,从 JavaScript 中的 URL 读取 JSON , Node Sass 教程, Lodash 教程。
