Open6
Mock Server Worker + vitest + happy-dom で proxy された相対URLをハンドルする方法
Mock Server Worker + vitest + happy-dom で API のモックしたユニットテストをしたい。
API は front の稼働してる URL の一部が backend に reverse proxy されているという特殊環境。
普通にやると以下のように実行時エラーがでる。
TypeError: Only absolute URLs are supported
❯ getNodeRequestOptions node_modules/node-fetch/lib/index.js:1327:9
❯ node_modules/node-fetch/lib/index.js:1440:19
❯ fetch node_modules/node-fetch/lib/index.js:1437:9
❯ node_modules/happy-dom/lib/window/Window.js:557:25
❯ Window.<anonymous> node_modules/happy-dom/lib/window/Window.js:548:39
❯ step node_modules/happy-dom/lib/window/Window.js:67:23
❯ Object.next node_modules/happy-dom/lib/window/Window.js:48:53
❯ node_modules/happy-dom/lib/window/Window.js:42:71
エラーが出るケース
モックハンドラ
import { rest } from 'msw'
export const handlers = [
rest.get('/hello.txt', (_req, res, ctx) => {
return res(ctx.status(200), ctx.text('Mock!'))
}),
]
テスト対象コンポーネント
<script setup lang="ts">
import { onMounted, ref } from 'vue'
const test = ref('')
onMounted(
async () =>
(test.value = await fetch('/hello.txt')
.then(async (res) => {
return await res.text()
})
.catch((e) => {
console.log('fetch error: ', e)
return 'error!'
}))
)
</script>
<template>
<div>Hello {{ test }}</div>
</template>
テストコード
import { render, waitFor, screen } from '@testing-library/vue'
import { server } from '../src/mocks/server'
import App from '../src/App.vue'
describe('describe', async () => {
beforeAll(() => {
server.listen()
})
afterEach(() => {
server.resetHandlers()
})
afterAll(() => {
server.close()
})
it('test1', async () => {
const wrapper = render(App)
expect(await wrapper.findAllByText('Hello Mock!')).toBeTruthy()
})
})
原因は happy-dom が使ってる node-fetch が absolute URL しか対応してない(node用なので)中で、相対URLを指定してるから。
対応として、happy-dom の window.location を beforeAll のときに適当に設定してあげ、かつハンドラ側のURLもそれに合わせることで解決できる。
解消した例。
テスト対象コンポーネント(App.vue) は同じなので割愛。
対応済みモックハンドラ
import { rest } from 'msw'
export const handlers = [
rest.get('http://localhost:3000/hello.txt', (_req, res, ctx) => { // !!! 絶対URLにしておく
return res(ctx.status(200), ctx.text('Mock!'))
}),
]
対応済みテストコード
import { render, waitFor, screen } from '@testing-library/vue'
import { server } from '../src/mocks/server'
import App from '../src/App.vue'
describe('describe', async () => {
beforeAll(() => {
window.location.href = 'http://localhost:3000' // !!! ブラウザの origin を設定しておく。fetchは相対URLを指定されるとこれを使う。
server.listen()
})
afterEach(() => {
server.resetHandlers()
})
afterAll(() => {
server.close()
})
it('test1', async () => {
const wrapper = render(App)
expect(await wrapper.findAllByText('Hello Mock!')).toBeTruthy()
})
})
vitest実行結果
$ yarn vitest --dom --environment=happy-dom --silent=false --run
RUN v0.7.7 /Users/coji/progs/spike/vitest/vitest-example
√ test/hello.spec.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Time 2.76s (in thread 33ms, 8483.38%)
✨ Done in 3.42s.
適当に設定するURLは development 環境用の URL に合わせると、web環境でも mock が機能してよい。
実験で使ったコードを github にあげときました。