vue+jest进阶分享

1. 背景

本文将介绍关于jest(单元测试)进阶的使用方法。

1.1 前置资料

2. mock

2.1 初识 mock

2.1.1 目的

  • 捕获函数调用情况
  • 设置函数返回值
  • 改变函数的内部实现

2.1.2 jest.fn

jest.fn(implementation)是创建 Mock 函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值。详情请查看这里

describe('test', () => {
  beforeEach(() => {})

  afterEach(() => {})

  beforeAll(() => {})

  afterAll(() => {})

  test('jest.fn', async () => {
    const callback = jest.fn()
    callback()
    expect(callback).toHaveBeenCalled()
  }, 5000)

  test('jest.fn default result', async () => {
    const callback = jest.fn()
    const result = callback()
    expect(result).toBeUndefined()
  }, 5000)

  test('jest.fn custom result', async () => {
    const callback = jest.fn(() => 123)
    const result = callback()
    expect(result).toBe(123)
  }, 5000)

  test('jest.fn mockReturnValue custom result', async () => {
    const callback = jest.fn().mockReturnValue(123)
    const result = callback()
    expect(result).toBe(123)
  }, 5000)

  test('jest.fn mockReturnValueOnce custom result', async () => {
    const callback = jest.fn()
    callback.mockReturnValueOnce(123)
    callback()
    const result = callback()
    expect(result).toBeUndefined()
  }, 5000)
})

2.1.3 jest.mock

当我们使用类似 axios 之类的库去请求 restful 数据时,在写单元测试的时候并不需要进行实际的请求,此时使用jest.mock(moduleName, factory, options)来伪造整个模块是十分有必要的。详情请查看这里

2.1.4 jest.spyOn

有时候我们既想使用jest捕获函数的调用情况,又想正常的执行被 spy 的函数。此时就可以用使用jest.spyOn(object, methodName)。详情请查看这里

2.1.5 jest.createMockFromModule

给定模块的名称,使用自动模拟系统生成模块的模拟版本。jest.genMockFromModule(moduleName)是它的别名。详情请查看这里

2.2 使用示例

2.2.1 child_process

在调用 child_process 模块时,有可能是非常耗时的操作或者是启动某个服务,此时如果不伪造child_process模块,有可能导致 Functions、Statements 无法覆盖的问题,因此此处提供伪造child_process模块的思路,以供读者参考。

// __mocks__/child-process.js
jest.mock('child_process', () => {
  return {
    exec (command, options, callback) {
      const EventEmitter = require('events')
      const stdout = new EventEmitter()
      const stderr = new EventEmitter()
      stdout.emit('data', 'stdout')
      stderr.emit('data', 'stderr')
      const child = new EventEmitter()
      child.stdout = stdout
      child.stderr = stderr
      setTimeout(callback, 500)
      setTimeout(() => child.emit('close', 'process closed'), 1000)
      return child
    },
    spawn (command, args, options) {
      const EventEmitter = require('events')
      const stdout = new EventEmitter()
      const stderr = new EventEmitter()
      stdout.emit('data', 'stdout')
      stderr.emit('data', 'stderr')
      const child = new EventEmitter()
      child.stdout = stdout
      child.stderr = stderr
      setTimeout(() => child.emit('close', 'process closed'), 1000)
      return child
    },
  }
})

// __tests__/child-process.test.js
require('@__mocks__/child-process')

const { exec, spawn } = require('child_process')

describe('child-process.', () => {
  beforeEach(() => {})

  afterEach(() => {})

  beforeAll(() => {})

  afterAll(() => {})

  test('exec callback', done => {
    const callback = jest.fn()
    exec('ls -1l', {}, () => {
      callback()
      expect(callback.mock.calls.length).toBe(1)
      done()
    })
  }, 5000)

  test('exec close', done => {
    const onClose = jest.fn()
    const child = exec('ls -1l', {}, () => {})
    child.on('close', () => {
      onClose()
      expect(onClose.mock.calls.length).toBe(1)
      done()
    })
  }, 5000)

  test('spawn close', done => {
    const onClose = jest.fn()
    const child = spawn('ls', ['-1l', '.'], {})
    child.on('close', () => {
      onClose()
      expect(onClose.mock.calls.length).toBe(1)
      done()
    })
  }, 5000)
})

2.2.2 sessionStorage/localStorage

本文用 nodejs 来演示sessionStorage/localStorage的测试,前端环境的 mock 方式与本文介绍的方法思路相同,在本文的基础上将 commonjs 的语法换成 es 的语法、将 window 对象的初始化去除即可。

// __mocks__/storage-data.js
module.exports = {
  storage_test: 'storage_test_value',
}

// __mocks__/storage.js
const StorageData = require('@__mocks__/storage-data')
const { merge } = require('lodash')

module.exports = class MockStorage {
  constructor () {
    this.store = merge({}, StorageData)
    this._updateProperties()
  }

  _updateProperties () {
    this.keys = Object.keys(this.store)
    this.length = this.keys.length
  }

  key (index) {
    return this.keys[index]
  }

  clear () {
    this.store = {}
    this._updateProperties()
  }

  getItem (key) {
    return this.store[key] || null
  }

  setItem (key, value) {
    this.store[key] = value.toString()
  }

  removeItem (key) {
    delete this.store[key]
  }
}

// __mocks__/local-storage.js
const MockStorage = require('@__mocks__/storage')

module.exports = class MockLocalStorage extends MockStorage {
  constructor () {
    super()
  }
}

// __mocks__/session-storage.js
const MockStorage = require('@__mocks__/storage')

module.exports = class MockSessionStorage extends MockStorage {
  constructor () {
    super()
  }
}

// __tests__/storage/local.test.js
const MockLocalStorage = require('@__mocks__/local-storage')

const window = {}

describe('localStorage', () => {
  beforeEach(() => {
    Object.defineProperty(window, 'localStorage', {
      writable: true,
      value: new MockLocalStorage(),
    })
  })

  afterEach(() => {})

  beforeAll(() => {})

  afterAll(() => {})

  test('key storage_test', async () => {
    const result = window.localStorage.key(0)
    expect(result).toBe('storage_test')
  })

  test('getItem storage_test', async () => {
    const result = window.localStorage.getItem('storage_test')
    expect(result).toBe('storage_test_value')
  })

  test('clear', async () => {
    window.localStorage.clear()
    const result = window.localStorage.key(0)
    expect(result).toBeUndefined()
  })

  test('setItem value_for_test', async () => {
    const newVal = 'value_for_test'
    window.localStorage.setItem('storage_test', newVal)
    const result = window.localStorage.getItem('storage_test')
    expect(result).toBe(newVal)
  })
})

// __tests__/storage/session.test.js
const MockSessionStorage = require('@__mocks__/session-storage')

const window = {}

describe('sessionStorage', () => {
  beforeEach(() => {
    Object.defineProperty(window, 'sessionStorage', {
      writable: true,
      value: new MockSessionStorage(),
    })
  })

  afterEach(() => {})

  beforeAll(() => {})

  afterAll(() => {})

  test('key storage_test', async () => {
    const result = window.sessionStorage.key(0)
    expect(result).toBe('storage_test')
  })

  test('getItem storage_test', async () => {
    const result = window.sessionStorage.getItem('storage_test')
    expect(result).toBe('storage_test_value')
  })

  test('clear', async () => {
    window.sessionStorage.clear()
    const result = window.sessionStorage.key(0)
    expect(result).toBeUndefined()
  })

  test('setItem value_for_test', async () => {
    const newVal = 'value_for_test'
    window.sessionStorage.setItem('storage_test', newVal)
    const result = window.sessionStorage.getItem('storage_test')
    expect(result).toBe(newVal)
  })
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿祥_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值