facebook出的一个通用测试框架。流行的框架都用它来测试。babel、typesctipt、node、react
- https://www.jestjs.cn/
-
Jest 的特点
开箱即用,零配置
- 快
- 内置代码覆盖率
- Mocking 很容易
Jest 实现异步测试
```javascript const { it, expect } = require(“@jest/globals”);
// callback const fetchUser = (cb) => { setTimeout(() => { cb(‘hello’) }, 100); } // 使用done参数,当完成回调后,再进行测试 it(‘test callback’, (done) => { fetchUser((data) => { expect(data).toBe(‘hello’) done() }) })
// promise const userPromise = () => Promise.resolve(‘hello’) // promise要使用return,否则在promise完成之前就执行完了,即使报错了,也会判定为passed it(‘test Promise’, () => { return userPromise().then(data => { expect(data).toBe(‘hello’) }) }) // await it(‘test with async’, async ()=>{ const data = await userPromise() expect(data).toBe(‘hello’) }) // resolves it(‘test with expect’, () => { return expect(userPromise()).resolves.toBe(‘hello’) }) // reject const rejectPromise = () => Promise.reject(‘error’) it(‘test with expect’, () => { return expect(rejectPromise()).rejects.toBe(‘error’) })
<a name="GQ4Iz"></a>
# Jest 实现 Mock
<a name="MQnJl"></a>
## 为什么要有Mock?
- 前端需要有网络请求
- 后端依赖数据库等模块
- 局限性:依赖其他的模块
<a name="AOAt9"></a>
## Mock 解决方案
- 测试替代,将真实代码替换为替代代码
<a name="wcaDB"></a>
## Mock 的两大功能
- 创建 mock function,在测试中使用,用来测试回调。
- 可以mock函数的属性,实现等。
- 手动 mock,覆盖第三方实现,狸猫换太子
<a name="ucEmB"></a>
## Jest mock 函数测试
```javascript
const { it, expect } = require("@jest/globals")
function mockTest(shouldCall, cb) {
if(shouldCall) {
return cb(42)
}
}
it('test with mock function', () => {
const mockCb = jest.fn() // 这个函数可以监控函数的调用情况
mockTest(true, mockCb)
// 是否有被调用过
expect(mockCb).toHaveBeenCalled()
// 是否被某个参数调用到
expect(mockCb).toHaveBeenCalledWith(42)
// 调用次数
expect(mockCb).toHaveBeenCalledTimes(1)
// 每次调用后的值
console.log(mockCb.mock.calls);
// 每次调用后返回的结果
console.log(mockCb.mock.results);
})
it('test mock with implementation', () => {
// const mockCb = jest.fn(x => x * 2)
const mockCb = jest.fn().mockReturnValue(20)
mockTest(true, mockCb)
console.log(mockCb.mock.results);
})
Jest mock 第三方模块实现
const getUserName = require('./user')
const axios = require('axios')
jest.mock('axios') // 接管axios的实现
// mockImplementation 模拟get的实现
axios.get.mockImplementation(() => {
return Promise.resolve({ data: {username: 'viking'}})
})
// axios.get.mockReturnValue(Promise.resolve({ data: {username: 'viking'}}))
axios.get.mockResolvedValue({ data: {username: 'viking'}})
it('test with mock modules', () => {
return getUserName(1).then(name => {
console.log(name);
expect(axios.get).toHaveBeenCalled()
expect(axios.get).toHaveReturnedTimes(1)
})
})
我们目前引用第三方模块的方式,如果有多个jest测试文件需要使用这个第三方模块,那我们需要在每个jest文件中进行模拟,不是很方便,jest提供了更简便的方式
- 在根目录下创建
__mocks__
文件夹 - 在文件夹中创建模块同名的文件
- 写入如下模拟函数 ```javascript const axios = { get: jest.fn(() => Promise.resolve({data: { username: ‘viking’ }})) }
module.exports = axios
- 在jest测试文件中使用,此时jest会默认找 `__mocks__` 下的模拟模块,不需要重复模拟第三方模块
```javascript
const getUserName = require('./user')
const axios = require('axios')
it('test with mock modules', () => {
return getUserName(1).then(name => {
console.log(name);
expect(axios.get).toHaveBeenCalled()
expect(axios.get).toHaveReturnedTimes(1)
})
})
Jest Timers Mock
- 关于事件的异步编程函数
- setTimeout,setInterval ….
- 需要将现实和测试分离开来
- Jest timer mocks
- 三大 API 完成不同粒度的时间控制
- jest.runAllTimers
- jest.runOnlyPendingTimers
- jest.advanceTimersByTime ```javascript const { expect } = require(“@jest/globals”);
const fetchUser = (cb) => { setTimeout(() => { cb(‘hello’) }, 1000); }
const loopFetchUser = (cb) => { setTimeout(() => { cb(‘one’) setTimeout(() => { cb(‘two’) }, 2000); }, 1000); } // 告诉整个测试,现在所有的timer,都由jest接管了 jest.useFakeTimers() it(‘test the callback after 1 sec’, () => { const callback = jest.fn() fetchUser(callback) expect(callback).not.toHaveBeenCalled() jest.runAllTimers() // 让所有的timers运行完毕 expect(callback).toHaveBeenCalledTimes(1) expect(callback).toHaveBeenCalled() expect(callback).toHaveBeenCalledWith(‘hello’) })
it(‘test the callback in timeout loops’, () => { const callback = jest.fn() loopFetchUser(callback) expect(callback).not.toHaveBeenCalled() jest.runOnlyPendingTimers() // 只运行当前等待的timer expect(callback).toHaveBeenCalledTimes(1) expect(callback).toHaveBeenLastCalledWith(‘one’) jest.runOnlyPendingTimers() expect(callback).toHaveBeenCalledTimes(2) expect(callback).toHaveBeenLastCalledWith(‘two’) })
it(‘test the callback with advance timer’, () => { const callback = jest.fn() loopFetchUser(callback) expect(callback).not.toHaveBeenCalled() jest.advanceTimersByTime(500) // 程序前进500毫秒 jest.advanceTimersByTime(500) // 程序前进500毫秒 expect(callback).toHaveBeenCalledTimes(1) expect(callback).toHaveBeenLastCalledWith(‘one’) jest.advanceTimersByTime(2000) expect(callback).toHaveBeenCalledTimes(2) expect(callback).toHaveBeenLastCalledWith(‘two’) }) ```