Vue.jsのテスト環境まわり

Vue.jsのテスト環境を整える。

jestを使えるようにします。
Vue CLIがグローバルではいっていて、すでにあるVue.jsのプロジェクトに追加する場合。
vue add unit-jest
package.json
のscripts
に追加します。
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
+ "test:unit": "vue-cli-service test:unit",
},
npm run
で実行します。
npm run test:unit
デフォルトだと下記のファイルが対象になります。
-
tests/unit
ディレクトリ以下にある、.spec.(js|jsx|ts|tsx)
で終わるファイル名 -
__tests__
というディレクトリ名にある、拡張子がjs(x)/ts(x)
のファイル

Mock Service Worker
Mock Service Workerは、Service WorkerのAPIを使って実際のリクエストをインターセプトしてAPIをモックするライブラリです。
jestで外部のAPIを呼ぶようなテストを書くときに便利です。
セットアップ
パッケージをインストールします。
npm install msw --save-dev
モック定義
リクエストを扱うためのハンドラー、ブラウザやサーバ特有のセットアップなどモックの動作を定義します。どのように整理をしてもいいのですが、ここではそれらを一つにまとめておくためのディレクトリを作成します。
mkdir src/mocks
ハンドラーを定義するためにファイルを作成します。
touch src/mocks/handlers.js

REST APIをモック
インポート
src/mocks/handlers.js
ファイルでは、REST APIをモックするために必要なものをインポートします。これらはライブラリで公開されているrest
名前空間の下にまとめられています。
import { rest } from 'msw'

リクエスト・ハンドラー
REST APIのリクエストを処理するには、メソッド、パス、そしてモックされたレスポンスを返す関数を指定する必要があります。
ここでは、ユーザのための基本的なログインのフローをモックします。このフローでは、2つのリクエストを処理します。
-
POST /login
:ユーザーのログインを許可します。 -
GET /user
:ログインしたユーザの情報を返します。
rest[METHOD]
を呼び出し、リクエストパスを指定してリクエストハンドラを作成します。
import { rest } from 'msw'
export const handlers = [
// Handles a POST /login request
rest.post('/login', null),
// Handles a GET /user request
rest.get('/user', null),
]

レスポンス・レゾルバ
傍受されたリクエストに応答するには、レスポンス・リゾルバ関数を使って、モックされたレスポンスを指定する必要があります。
レスポンス・リゾルバは、次のような引数を受け取る関数です。
-
req
:一致するリクエストに関する情報。 -
res
:モックされたレスポンスを作成するための機能的なユーティリティ。 -
ctx
:モックアップされたレスポンスのステータスコード、ヘッダー、ボディなどを設定するための関数群。
先に定義したリクエスト・ハンドラにレスポンス・リゾルバを提供する。
import { rest } from 'msw'
export const handlers = [
rest.post('/login', (req, res, ctx) => {
// Persist user's authentication in the session
sessionStorage.setItem('is-authenticated', 'true')
return res(
// Respond with a 200 status code
ctx.status(200),
)
}),
rest.get('/user', (req, res, ctx) => {
// Check if the user is authenticated in this session
const isAuthenticated = sessionStorage.getItem('is-authenticated')
if (!isAuthenticated) {
// If not authenticated, respond with a 403 error
return res(
ctx.status(403),
ctx.json({
errorMessage: 'Not authorized',
}),
)
}
// If authenticated, return a mocked user details
return res(
ctx.status(200),
ctx.json({
username: 'admin',
}),
)
}),
]
sessionStorage、localStorage、IndexedDBなどを利用して、より複雑なAPIシナリオやユーザーのインタラクションを処理します。

統合
同じリクエストハンドラをブラウザ環境とNode環境で共有することができます。サービスワーカーはNodeで実行できないので、統合のやり方は環境によって異なります。

ブラウザ
セットアップ
Mock Service Workerは、リクエストの傍受を行うService Workerを登録することで、クライアントサイドで動作します。しかし、ワーカーのコードを自分で書く必要はなく、ライブラリから配布されているワーカーのファイルをコピーすればよいのです。Mock Service Workerは、そのための専用のCLIを提供しています。
Mock Service WorkerのCLIのinitコマンドを実行します。
$ npx msw init public/ --save
ここでは、--save
オプションを使って、package.jsonに指定したワーカーディレクトリ(「public」)を保存していることに注意してください。これにより、将来的にmsw
パッケージを更新する際に、ワーカースクリプトの更新が自動的に適用されます。
ワーカーの設定
モックの定義ディレクトリ(src/mocks)に、Service Workerを設定・起動するためのファイルを作成しましょう。
src/mocks/browser.js
ファイルを作成します。
touch src/mocks/browser.js
browser.js
ファイルでは、先に定義したリクエストハンドラを持つWorkerインスタンスを作成します。
msw
パッケージからsetupWorker
関数をインポートし、先に定義したリクエスト・ハンドラを持つワーカー・インスタンスを作成します。
import { setupWorker } from 'msw'
import { handlers } from './handlers'
// This configures a Service Worker with the given request handlers.
export const worker = setupWorker(...handlers)
ワーカーを起動
モックの定義をランタイム中に実行するためには、アプリケーションのコードにインポートする必要があります。しかし、モックは開発向けの技術であるため、src/mocks/browser.js
ファイルを現在の環境に応じて条件付きでインポートすることになります。
以下の例にしたがって、src/mocks/browser.js
ファイルを条件付きでインポートします。
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}
ReactDOM.render(<App />, document.getElementById('root'))
ワーカーの起動は非同期に行われるため、マウント時にリクエストを行うアプリケーションとの間に競合状態が生じる可能性があります。そのような場合は、Deferred mountingレシピを参照して、ワーカーの準備ができたときにアプリケーションのマウントを強制してください。
検証と検査
モック定義をインポートした後、ブラウザのコンソールにMock Service Workerからの起動成功メッセージが表示されるはずです。
[MSW] Mocking enabled
先に定義したハンドラにマッチするリクエストは、すべてインターセプトされ、モック化されます。

Node
NodeにおけるMock Service Workerの最も一般的な使用法の一つは、統合テストにリクエスト・ハンドラを利用することです。ここでは、テストランナーとしてJestを使用します。同じ原理で、Nodeのどのプロセスにもモックを統合することができます。
サーバを設定
モックの定義ディレクトリ(src/mocks)に、リクエストモックのサーバーを設定するファイルを作成しましょう。
src/mocks/server.js
ファイルを作成します。
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
// This configures a request mocking server with the given request handlers.
export const server = setupServer(...handlers)
セットアップ
セットアップモジュールを作成し、jest.config.js
のsetupFilesAfterEnv
オプションに設定してください。
テスト用のセットアップファイルjest.setup.js
を作成します。
touch jest.setup.js
import { server } from './mocks/server.js'
// Establish API mocking before all tests.
beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
// Clean up after the tests are finished.
afterAll(() => server.close())
jest.config.js
ファイルに追加します。
module.exports = {
preset: '@vue/cli-plugin-unit-jest',
transform: {
'^.+\\.vue$': 'vue-jest'
},
+ setupFilesAfterEnv: ["./jest.setup.js"],
}
テストを実行
APIのモック化はテストの設定で確立されているので、各テストスイートは、ハンドラーに応じてAPIリクエストをインターセプトし、モック化するための特別な調整は必要ありません。
test('allows user to log in', async () => {
// Render components, perform requests, receive mocked responses.
})
直接の使用方法
Mock Service WorkerのsetupServer APIは、任意のNodeJSアプリケーションで使用することができます(例えば、Expressサーバの開発やテストを行う場合など)。
JestのjsdomのようなDOMライクな環境がない場合、NodeJSでは絶対的なリクエストURLを使用しなければならないことを覚えておいてください。これは、リクエスト・ハンドラに反映させる必要があります。
const server = setupServer(
// NOT "/user", nothing to be relative to!
rest.get('https://api.backend.dev/user', (req, res, ctx) => {
return res(ctx.json({ firstName: 'John' }))
}),
)