vuex_typescriptでjest

middlewareをテストする場合を考える。

~/middleware/user.ts

import { Context, Middleware } from '@nuxt/types'
import { user } from '~/store'

const middleware: Middleware = (context: Context) => {
  // ページ遷移のたびに実行したい処理を書く
  if (user.expiration > new Date()) {
    user.update(false)
    context.redirect('/error')
  }
}

export default middleware

~/test/middleware/user.spec.ts

import { createLocalVue } from '@vue/test-utils'
import { MockProxy, mock } from 'jest-mock-extended'
import MockDate from 'mockdate'
import Vuex, { Store } from 'vuex'
import { Context } from '@nuxt/types'
import { initializeStores, user } from '~/store'
import middleware from '~/middleware/user'

describe('middleware', () => {
  const localVue = createLocalVue()
  localVue.use(Vuex)

  // jest-mock-extended を使って @nuxt/types/Context をmock
  let mockContext: MockProxy<Context>

  let store: Store<{}>
  let getters

  // @nuxt/types/Middleware が string | Function のunion typeなためエラーになるので、型を絞り込む
  // This expression is not callable.
  // Not all constituents of type 'Middleware' are callable.
  // Type 'string' has no call signatures
  const middlewareFunc = middleware as (ctx: Context) => Promise<void> | void

  beforeEach(() => {

    // gettersをmock
    getters = {
      'user/expiration': jest.fn(() => new Date(1614700000)),
    }
    // Vuexの初期化
    store = new Vuex.Store({
      state: {},
      getters,
    })
    initializeStores(store)

    mockContext = mock<Context>()
  })

  it('success', () => {

    // mockdate を使用してnew Date()をmock
    MockDate.set('2021-01-02T15:04:05+09:00')

    user.update = jest.fn()

    // context.redirect をmock
    ;(mockContext.redirect as jest.Mock) = jest.fn(() => {})

    middlewareFunc(mockContext)

    expect(mockContext.redirect).toHaveBeenCalledTimes(0)
  })

})