created: 2019-10-16 00:00:00,tags:javascript,test,jest,

JS でのリダイレクト処理のテストを Jest でやる

Vue.js を使った SPA サービス上で、未ログイン時のリダイレクト処理を実装したけど、 そのテスト方法が欲しかった。

多分、何も考えなかったら下記のようなコードで飛ばしたりすると思う。

location.href = '/hoge/huga'

これだとリダイレクトをかける関数のテストが出来ず困るので、関数で指定出来る方が望ましく、リダイレクトには location.assign を使用した。

対応方法

テストに Jest を使用しているため、他のやり方は知らないけど、多分考え方は変わらないと思う。

assign を使用する理由として、関数である事が望ましいという事を前述したけど、それは mock で実装を差し替えるため。 実際にテストを書くと下記のようになる。

function redirectTo() {
  location.assign('/login')
}

test('jest で redirect の test', () => {
  window.location.assign = jest.fn()
  expect(location.assign).toBeCalledWith(redirectTo)
})

このやりかたの場合、redirectTo が引数を取っていないのと、window オブジェクトを変更してしまっているため元に戻さないといけないという問題が発生する (global なものを変更して他に影響がでる可能性があるので) ため、少し問題がある。

解決方法を調べた所、jest の mock は実装が変更された際など、初期値に戻すための機能があるので、それを使う事で window オブジェクトの変更も戻せた。

ただ、jest.fn で生成したオブジェクトから実行できる implementation だと、指定オブジェクトの前の状態を記憶する事ができない。 そのため、別機能の spyOn を利用する。

function redirectTo(url) {
  location.assign(url)
}

test('jest で redirect の test', () => {
  // window.location.assign を mock に書き変えてテストを行なう
  // spyOn で空の mock 関数を定義、呼び出し時の引数をテストする
  const redirectPage = '/system/login'
  const windowLocationAssignBackup = window.location.assign

  jest.spyOn(window.location, 'assign')
    .mockImplementation(() => {})

  redirectTo('/login')
  expect(location.assign).toBeCalledWith('/login')

  // 他のテストに影響が出る可能性があるため、元に戻しておく
  window.location.assign.mockRestore()
})

spyOn は指定したオブジェクトの mock オブジェクトを作成でき、mockImplementation で渡したコールバックで処理を上書きできる。 これを使う事で指定のオブジェクトが上書き前の状態を記憶する事ができるため、テスト終了後に mockRestore() を実行して元に戻る。

また、引数の問題に関しては、mock で差し替えたオブジェクトが呼ばれる際に与えられた引数を記憶しているため、 toBeCalledWith を利用して、直前に実行された関数呼び出しの引数をテストできる。