facebook出的一个通用测试框架。流行的框架都用它来测试。babel、typesctipt、node、react

  • https://www.jestjs.cn/
  • https://jestjs.io/

    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’) })

  1. <a name="GQ4Iz"></a>
  2. # Jest 实现 Mock
  3. <a name="MQnJl"></a>
  4. ## 为什么要有Mock?
  5. - 前端需要有网络请求
  6. - 后端依赖数据库等模块
  7. - 局限性:依赖其他的模块
  8. <a name="AOAt9"></a>
  9. ## Mock 解决方案
  10. - 测试替代,将真实代码替换为替代代码
  11. <a name="wcaDB"></a>
  12. ## Mock 的两大功能
  13. - 创建 mock function,在测试中使用,用来测试回调。
  14. - 可以mock函数的属性,实现等。
  15. - 手动 mock,覆盖第三方实现,狸猫换太子
  16. <a name="ucEmB"></a>
  17. ## Jest mock 函数测试
  18. ```javascript
  19. const { it, expect } = require("@jest/globals")
  20. function mockTest(shouldCall, cb) {
  21. if(shouldCall) {
  22. return cb(42)
  23. }
  24. }
  25. it('test with mock function', () => {
  26. const mockCb = jest.fn() // 这个函数可以监控函数的调用情况
  27. mockTest(true, mockCb)
  28. // 是否有被调用过
  29. expect(mockCb).toHaveBeenCalled()
  30. // 是否被某个参数调用到
  31. expect(mockCb).toHaveBeenCalledWith(42)
  32. // 调用次数
  33. expect(mockCb).toHaveBeenCalledTimes(1)
  34. // 每次调用后的值
  35. console.log(mockCb.mock.calls);
  36. // 每次调用后返回的结果
  37. console.log(mockCb.mock.results);
  38. })
  39. it('test mock with implementation', () => {
  40. // const mockCb = jest.fn(x => x * 2)
  41. const mockCb = jest.fn().mockReturnValue(20)
  42. mockTest(true, mockCb)
  43. console.log(mockCb.mock.results);
  44. })

Jest mock 第三方模块实现

  1. const getUserName = require('./user')
  2. const axios = require('axios')
  3. jest.mock('axios') // 接管axios的实现
  4. // mockImplementation 模拟get的实现
  5. axios.get.mockImplementation(() => {
  6. return Promise.resolve({ data: {username: 'viking'}})
  7. })
  8. // axios.get.mockReturnValue(Promise.resolve({ data: {username: 'viking'}}))
  9. axios.get.mockResolvedValue({ data: {username: 'viking'}})
  10. it('test with mock modules', () => {
  11. return getUserName(1).then(name => {
  12. console.log(name);
  13. expect(axios.get).toHaveBeenCalled()
  14. expect(axios.get).toHaveReturnedTimes(1)
  15. })
  16. })

我们目前引用第三方模块的方式,如果有多个jest测试文件需要使用这个第三方模块,那我们需要在每个jest文件中进行模拟,不是很方便,jest提供了更简便的方式

  • 在根目录下创建 __mocks__ 文件夹
  • 在文件夹中创建模块同名的文件

image.png

  • 写入如下模拟函数 ```javascript const axios = { get: jest.fn(() => Promise.resolve({data: { username: ‘viking’ }})) }

module.exports = axios

  1. - jest测试文件中使用,此时jest会默认找 `__mocks__` 下的模拟模块,不需要重复模拟第三方模块
  2. ```javascript
  3. const getUserName = require('./user')
  4. const axios = require('axios')
  5. it('test with mock modules', () => {
  6. return getUserName(1).then(name => {
  7. console.log(name);
  8. expect(axios.get).toHaveBeenCalled()
  9. expect(axios.get).toHaveReturnedTimes(1)
  10. })
  11. })

Jest Timers Mock

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’) }) ```