Quick
Start
安装:
npm install --save-dev jest
让我们开始为一个假设函数编写测试,该函数将两个数字相加。 首先,创建一个 sum.js
文件:
function sum(a, b) {return a + b;
}
module.exports = sum;
然后,创建一个名为 sum.test.js
的文件。jest会自动找对应的test
作为测试文件,所以我们这里也使用了.test
文件名。 这将包含我们的实际测试:
const sum = require('./sum');test('adds 1 + 2 to equal 3', () => {expect(sum(1, 2)).toBe(3);
});
- test方法:需要测试的代码
- expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得到的预期是什么
将下面的配置部分添加到你的 package.json
里面:
{"scripts": {"test": "jest"}
}
最后,运行 yarn test
或 npm run test
,Jest将打印下面这个消息:
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)
若需要每次修改控制台就会自动跑测试代码,可配置:
{"scripts": {"test": "jest --watchAll"}
}
测试覆盖率生成
在jest.config.js里面配置coverageDirectory : "coverage"
,coverageDirectory为输出覆盖信息文件的目录
const {defaults} = require('jest-config');
module.exports = {moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],coverageDirectory : "coverage"
};
使用npx jest --coverage
即可生成一个代码测试覆盖率的说明
让jest支持ES6
由于jest不支持ES6,需要配置babel进行ES6的转换
安装依赖:
npm install --save-dev babel-jest @babel/core @babel/preset-env
可以在工程的根目录下创建一个babel.config.js
文件用于配置与你当前Node版本兼容的Babel:
// babel.config.js
module.exports = {presets: [['@babel/preset-env',{targets: {node: 'current',},},],],
};
创建jest.config.js
// jest.config.js
const {defaults} = require('jest-config');
module.exports = {moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx']
};
jest中的匹配器(详细可查看官网)
toBe
使用 Object.is 判断是否严格相等。
toEqual
递归检查对象或数组的每个字段。
toBeNull
只匹配 null
。
toBeUndefined
只匹配 undefined
。
toBeDefined
只匹配非 undefined
。
toBeTruthy
只匹配真。
toBeFalsy
只匹配假。
toBeGreaterThan
实际值大于期望。
toBeGreaterThanOrEqual
实际值大于或等于期望值
toBeLessThan
实际值小于期望值。
toBeLessThanOrEqual
实际值小于或等于期望值。
toBeCloseTo
比较浮点数的值,避免误差。
toMatch
正则匹配。
toContain
判断数组中是否包含指定项。
toHaveProperty(keyPath, value)
判断对象中是否包含指定属性。
toThrow
判断是否抛出指定的异常。
toBeInstanceOf
判断对象是否是某个类的实例,底层使用 instanceof
。
jest进行异步测试(详细可查看官网)
有一个异步请求接口fetchData,对其进行单元测试,代码如下:
//fetchData.js
import axios from 'axios';export const fetchData = (fn)=>{axios.post('接口').then((response)=>{fn(response.data.responseCode);})
}
//fetchData.test.js
import { fetchData } from '../fetchData.js'test('fetchData 测试',()=>{fetchData((res)=>{expect(res).toEqual('接口返回的值');})
})
注意这样写是有问题的,因为方法还没有等到回调,我们的结果已经完成了,所以这时候你对于没测试完,只是方法可用,就返回了测试结果,这种结果是不保证正确的。
方法一:使用单个参数调用 done
,而不是将测试放在一个空参数的函数。 Jest会等done
回调函数执行结束后,结束测试。
//fetchData.test.js
import { fetchData } from '../fetchData.js'test('fetchData 测试',(done)=>{fetchData((res)=>{expect(res).toEqual(str);done();})
})
方法二:使用Promise的方式,fetchData
不使用回调函数,而是返回一个 Promise
//fetchData.js
export const fetchData = () => {return axios.post('接口');
}
返回一个promise
//fetchData.test.js
test('fetchData 测试', () => {return fetchData().then(res => {expect(res.data.responseCode).toEqual('接口返回的值'); //res.data.responseCode 接口自定义的数据结构
})
不要忘记把 promise 作为返回值⸺如果你忘了 return
语句的话,在 fetchData
返回的这个 promise 被 resolve、then() 有机会执行之前,测试就已经被视为已经完成了。
同样的,可以通过.catch来测试异常情况
//fetchData.test.js
test('fetchData 测试', () => {return fetchData().catch(err => {expect(err).toEqual('error');
})
这里需要说明一下,只有出现异常的时候才会走这个方法,若没有出现异常,就不会走这个测试方法,而Jest会默认这个用例通过了测试。因此需要使用expect.assertions(1)
,这个代码的意思是“断言,必须需要执行一次expect方法才可以通过测试”。
//fetchData.test.js
test('fetchData 测试', () => {expect.assertion(1);return fetchData().catch(err => {expect(err).toEqual('error');
})
**方法三:**使用async/await方式
//fetchData.js
export const fetchData = () => {return axios.post('接口');
}
返回一个promise
//fetchData.test.js
test('fetchData 测试', async () => {let res = await fetchData();expect(res.data.responseCode).toEqual('接口返回的值');
})
jest使用Mock进行测试(详细可查看官网)
jest提供了Mock方法,可以通过jest.fn()编写mock方法
const myMock = jest.fn(res => {console.log('mock....')});myMock(); // mock....
通过jest.mock()对已有的方法或模块进行mock
// foo.js
export const foo = () => {//do something...
}// test.js
import { foo } from './foo'
jest.mock('./foo'); //mock foo foo.mockImplementation(() => 42);
foo(); // 42
或者可以这样
// foo.js
export const foo = () => {//do something...
}// test.js
import { foo } from './foo'
jest.mock('./foo', () => { //mock foo return {foo: jest.fn(() => 42);}
});foo(); // 42
注: jest不支持ES6,若要使用import、export等需要进行babel转换
Q&A
1.jest.mock()模块工厂不允许引用任何范围外的变量
例如:
import { demo } from './demo';
import { foo } from './foo';
jest.mock('./foo', () => {demo();
});
此时会报错
The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.Invalid variable access: strAllowed objects: Array, ArrayBuffer, Atomics, BigInt, BigInt64Array, BigUint64Array, Boolean, Buffer, COUNTER_HTTP_CLIENT_REQUEST, COUNTER_HTTP_CLIENT_RESPONSE, COUNTER_HTTP_SERVER_REQUEST, COUNTER_HTTP_SERVER_RESPONSE, COUNTER_NET_SERVER_CONNECTION, COUNTER_NET_SERVER_CONNECTION_CLOSE, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DataView, Date, Error, EvalError, Float32Array, Float64Array, Function, GLOBAL, Generator, GeneratorFunction, Infinity, Int16Array, Int32Array, Int8Array, InternalError, Intl, JSON, Map, Math, NaN, Number, Object, Promise, Proxy, RangeError, ReferenceError, Reflect, RegExp, Set, SharedArrayBuffer, String, Symbol, SyntaxError, TypeError, URIError, URL, URLSearchParams, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, WeakMap, WeakSet, WebAssembly, arguments, clearImmediate, clearInterval, clearTimeout, console, decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, escape, eval, expect, global, isFinite,
isNaN, jest, parseFloat, parseInt, process, require, root, setImmediate, setInterval, setTimeout, undefined, unescape.Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` (case insensitive) are permitted.
解决方案:
可使用jest.requireActual()
import { demo } from './demo';
import { foo } from './foo';
jest.mock('./foo', () => {const res = jest.requireActual('./demo')res.demo();
});
参考
https://jestjs.io/docs/zh-Hans/getting-started