LoginSignup
25
27

More than 3 years have passed since last update.

Vue.jsでaxiosをモック化してテストする!

Last updated at Posted at 2019-12-18

axiosのモック化にかなり手間取ったため、作業メモとして残しておこうと思いました。

必要なライブラリ

  • @vue/cli-plugin-unit-jest
  • @vue/test-utils
  • flush-promises

jestを使ってモック化するため、cli-plugin-unit-jestを入れました。

最後のflush-promisesはVue Test Utilsの公式サイトで非同期動作のテクニックとして紹介されており、非同期処理を強制的に(?)実行させるためにインストールしました。
非同期のテスト

テストコード

axiosのインポート

import axios from 'axios'

まずはaxiosモジュールをインポートします。

システムによっては、axiosの設定をindex.jsに書いていると思いますが、テストコードではこのindex.jsではなく、生(と言っていいかは謎ですが)のaxiosをimportします。
理由としては、index.jsをimportすると、そのファイルに書かれている、例えばaxios.create等のメソッドもモック化しないといけないからです。
もちろん、別でindex.jsのテストは必要なのですが、今回のテスト対象は、axios.postやgetを使っているコンポーネントであるため、index.jsではなく、axiosをimportします。

axiosのモック化

jest.mock('axios')

axiosモジュールをモック化します。
これで、プロダクトコードでaxiosを呼び出した場合、これから登録するモック関数が呼ばれるようになります。
このjest.mock('axios')は、describeの前に実行してください。

mountまたはshallowMountの実行時オプションにsync: falseを付ける

const wrapper = mount(sampleComponent, { sync: false })

axios.postにモック関数を登録する

例えばこのようなプロダクトコードがあるとします。

methods: {
  aFunc() {
    axios.post('/sample')
      .then(response => {
        console.log('成功')
      })
      .catch(error => {
        console.log('失敗')
      })
  }
}

このaxios.postから返すレスポンスをモックにします。

const response = {
  message: '成功'
}
axios.post.mockImplementationOnce((url) => {
  return Promise.resolve(response)
})

mockImplementationOnceは、postが呼び出されたときに「一度だけ」関数の中が実行されます。

もし、テスト対象のプロダクトコードでpostが2回呼び出されていた時には、1回目のpostは上記で設定したPromise.resolveが返りますが、2回目のpostはエラーになります。
この場合の解決策としては2つあります。それについては後述します。
ちなみにプロダクトコードでthis.$httpのように使われている場合は、テストコードのimport文直後くらいにprototype.$http = axiosを入れてください。
私はcreateLocalVueでlocalVueを作成し、それに対して定義してます。

const localVue = createLocalVue()

localVue.prototype.$http = axios

モック関数の他例

もし、テスト対象のプロダクトコードでpostが2回呼び出されていた時には、1回目のpostは上記で設定したPromise.resolveが返りますが、2回目のpostはエラーになります。

例えばこのようなコードがあった場合は、mockImplementationOnceを一つ指定しただけだとエラーになります。

methods: {
  aFunc() {
    axios.post('/sample')               // post呼び出し1回目
      .then(response => {
        console.log('/sample成功')
        axios.post('/sample2')          // post呼び出し2回目
          .then(response => {
            console.log('/sample2成功')
          })
          .catch(error => {
            console.log('/sample2失敗')
          })
      })
      .catch(error => {
        console.log('/sample失敗')
      })
  }
}

ではどうすればよいか、ですが、求めるレスポンスによって2通りのやり方があります。

レスポンスが同じでも良い場合

jest.fn().mockImplementationを使います。

const response = {
  message: '成功'
}
axios.post.mockImplementation((url) => {
  return Promise.resolve(response)
})

こうするとaxios.postが呼ばれたときには 常にresponseが返されます。

レスポンスが異なる場合

jest.fn().mockImplementationOnceを呼び出されるAPIの数分定義します。

const response1 = {
  message: '成功'
}
const response2 = {
  message: '失敗'
}
axios.post.mockImplementationOnce((url) => {
  return Promise.resolve(response1)
}).mockImplementationOnce((url) => {
  return Promise.resolve(response2)
})

こうすることで、1回目のAPI呼び出しのときはresponse1が返り、2回目のAPI呼び出しの時はresponse2が返ります。

モックの検証

きちんと想定通りのモックが呼ばれているかを検証します。
検証したいことによって、いろいろとメソッドが用意されていますが、ここではtoHaveBeenCalledWithを使います。
その他のメソッドについてはJestのドキュメントをご覧ください。

レスポンスが異なる場合の検証をしてみましょう。

expect(axios.post).toHaveBeenCalledWith('/sample')
expect(axios.post).toHaveBeenCalledWith('/sample2')

expectの引数には、実行したモックを指定します。
toHaveBeenCalledWithの引数には、APIが実行されたときの引数を指定します。
今回、API実行時にはurlのみを引数としてaxios.postに渡しているため、それを指定します。
params等の引数を第2引数に渡している場合は、expect(axios.post).toHaveBeenCalledWith('/sample', { param1: 'aaa' })のように渡します。

axiosではなく作ったモジュールの一部をモック化する!

  1. モジュールを読み込む
import sampleModule from '@/mixin/sampleModule.js'
  1. jest.mockでモック化する
jest.mock('@/mixin/sampleModule')
  1. モック化したいメソッドにjest.fnを入れ込む
sampleModule.methods.sampleMethod = jest.fn((arg1, arg2) => {
  return data
})

これでモック化できます。

テスト対象コンポーネントのメソッドをモック化する!

  1. jest.mockでモック化する
const sampleMethod = jest.fn()
  1. mountオプションにmethodsを指定する
const wrapper = mount(sampleComponent, {
  localVue,
  methods: { sampleMethod }
})

ちゃんと調べたわけではないのですが、どうやらconstの名前のメソッドがモック化されるようです。

以上です!

25
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
27