Jest 是一个 javacript 测试框架,特点是简单易上手
安装
需要 node 9.2 以上版本,因为 9.2 以下的版本不支持 try…catch 语法,运行时会报语法错误
Matcher
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| expect(2 + 2).toBe(4);
const data = {one: 1}; data['two'] = 2; expect(data).toEqual({one: 1, two: 2});
expect(4).not.toBe(3);
expect('team').not.toMatch(/I/); expect('Christoph').toMatch(/stop/);
expect([1, 2]).toContain(1);
|
异步处理
回调模式
使用 done 来处理回调模式的异步函数
1 2 3 4 5 6 7 8 9 10 11 12
| test('callback pattern', done => { function callback(data) { try { expect(data).toBe('something'); done(); } catch (error) { done(error); } } fetchData(callback); });
|
Promise 模式
返回 Promise 即可;
1 2 3 4 5 6
| test("promise pattern", () => { return fetchData().then(data => { expect(data).toBe('something'); }) })
|
Async/Await 模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| test('Async pattern', async () => { const data = await fetchData(); expect(data).toBe('something'); });
test('Async pattern with error', async () => { try { const data = await fetchData(); expect(data).toBe('something'); } catch (error) { expect(error).toMatch('error'); } })
|
初始化和清理
重复初始化
1 2 3 4 5 6 7
| beforeEach(() => { initDB(); });
afterEach(() => { clearDB(); })
|
一次性初始化
1 2 3 4 5 6 7
| beforeAll(() => { return initDB(); })
afterAll(() => { return clearDB(); })
|
作用域
使用 describe 来建立块的作用域
1 2 3 4 5 6 7 8 9 10 11
| beforeEach('parent'); afterEach('parent'); test('parent1'); test('parent2'); describe('block', () => { beforeEach('child'); afterEach('child'); test('child1'); test('child2'); });
|
顺序控制
所有的 describe 将在所有的 test 之前先执行;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| describe('outer', () => { console.log('outer a'); describe('inner 1', () => { console.log('inner 1'); test('test 1', () => { console.log('test 1 inner 1'); }); }); console.log('outer b'); test('test 1', () => { console.log('test 1 outer'); }); describe('inner 2', () => { console.log('inner 2'); test('test 2', () => { console.log('test 2 inner 2'); }); }); console.log('outer c'); });
|
单例测试
test.only 方法可以使得只有该测试用例被执行;如果多例同时测试失败,但是单例测试可以成功,则说明某两个测试用例之间存在共享的变量,二者在测试过程中出现相互干扰的情况;
Mock 函数
mock 函数扮演拦截的作用,当在代码中对某个实际的函数进行调用时,拦截该调用,触发提前写好的 mock 函数,返回预设的结果;
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const mockFunc = jest.fn(x => 42 + x); forEach([0, 1], mockFunc);
expect(mockFunc.mock.calls.length).toBe(2);
expect(mockFunc.mock.calls[0][0]).toBe(0);
expect(mockFunc.mock.calls[1][0]).toBe(1);
expect(mockFunc.mock.results[0].value).toBe(42);
|
模拟返回值
1 2 3 4 5 6 7
| const mock = jest.fn(); console.log(mock());
mock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true); console.log(mock(), mock(), mock(), mock());
|
模拟函数在多次连续调用时,返回不同的值
1 2 3 4 5 6 7 8 9 10 11 12
| const filterMock = jest.fn();
filterMock.mockReturnValueOnce(true).mockReturnValueOnce(false);
const result = [11, 12].filter(num => filterMock(num));
console.log(result); console.log(filterMock.mock.calls[0][0]); console.log(filterMock.mock.calls[1][0]);
|
模拟模块的返回值
1 2 3 4 5 6 7 8 9
|
import axios from 'axios';
Class Users { static all() { return axios.get('/user.json').then(resp => resp.data); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| import Users from './users.js'; import axios from 'axios';
jest.mock('axios');
test('should fetch users', () => { const users = [{name: 'bob'}]; const resp = {data: users}; axios.get.mockResolvedValue(resp); return Users.all().then(data => expect(data).toEqual(users)); })
|
模拟模块的实现
1 2 3 4 5 6 7 8 9 10 11 12
| module.exports = function () { }
jest.mock('../foo'); const foo = require('../foo');
foo.mockImplementation(() => 42); foo();
|
模拟多次调用时,函数表现不同的行为
1 2 3 4 5 6 7 8 9
| const mockFn = jest.fn() .mockImplementationOnce(cb => cb(null, true)) .mockImplementationOnce(cb => cb(null, false));
mockFn((err, val) => console.log(val));
mockFn((err, val) => console.log(val));
|
模块多次调用时,不同的行为 + 默认的行为
1 2 3 4 5 6 7
| const myMockFn = jest .fn(() => 'default') .mockImplementationOnce(() => 'first call') .mockImplementationOnce(() => 'second call');
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
|
为模拟函数添加名称
通过定义模拟的函数的名称,在测试的输出信息中,能够更清晰的定位哪个函数出错了
1 2 3 4
| const mockFn = jest.fn() .mockReturnValue('default') .mockImplementation(scalar => scalar + 42) .mockName('add42');
|
快照
用来比对界面是否与预期的一致;典型的使用方法是给某个 UI 组件预先存一份快照,然后与测试过程中生成的界面进行比对,检查二者是否一致,若一致,表示应用正常;如不一致,说明界面出现了意外的变化,此时有可能是应用出现异常,或者 快照本身需要更新;