mswはAPIモーキングライブラリで、サーバーに向けたネットワーク要求をインターセプトして模擬応答(mocked response)を返す役割を果たします。
下の写真は、ブラウザがrequestを送るとservice workerがキャッチして、MSWを通してモッキングされたレスポンスをブラウザに送ります。
実際は、実物のAPIサーバからレスポンスを受けるが正しいだが、MSWを使ってリクエストをキャッチして任意のレスポンスを返すことができます。
そのrequestは実際のサーバーではないmswに送った後、登録されたhandlerを通じて模擬応答をクライアントに送ります。
msw setup
npm install msw --save
npx msw init public/ --save
publicのフォルダにmockServiceWorkerファイルを生成してくれるcommandです。
/public/mockServiceWorker.jsとは、ファイルができ、そのファイルがサービス・ウォーカーにmockサーバーを登録させます。このファイルはネットワークの要求をinterceptできるように設計されたservice worker apiを活用します。
browser.js定義
setupWorker()関数を使用してservice workerを生成します。関数の買収はhandler.jsに定義したハンドラを渡します。
import { setupWorker } from 'msw'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers); //定義handler登録
handlers.js定義
handlers.jsは要求が入って来た時、模擬の回答をしてくれるハンドラ(handler)コードを作成しなければなりません。
下のコードは、login requestと...raw.githubu...requestをinterceptしてmockingしています。
import { rest } from 'msw'
export const handlers = [
rest.get('/login', async (req, res, ctx) => {
return res(
ctx.json({
id: 'f79e82e8-c34a-4dc7-a49e-9fadc0979fda',
firstName: 'John',
lastName: 'Maverick',
})
)
}),
rest.get('https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json', async (req, res, ctx) => { // interceptするURLを入力!
return res(
{
"data": {
"people" :
[
{
"name": "jung",
"age": 135
},
{
"name": "yeounjae",
"age": 13
},
{
"name": "cindy",
"age": 15
},
{
"name": "judy",
"age": 25
},
{
"name": "marry",
"age": 64
},
{
"name": "tommy",
"age": 109
}
]
}
}
)
)
})
]
index.js定義
index.jsにはweb applicationが実行されるとき、service workerも実行されるようにしたコードを追加します。// Start the mocking conditionally.
// service mockを実行されるコード!
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}
Mockingテスト
import React, {useState} from 'react'
const Item = ({ name, age }) => {
return (
<li>
name : {name} /age : {age}
</li>
);
}
const url =
"https://raw.githubusercontent.com/techoi/raw-data-api/main/simple-api.json";
export default function TestMocking() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const handleClick = () => {
fetch(url)
.then((response) => {
return response.json();
})
.then((json) => {
setData(json.data);
})
.catch((error) => {
setError(`something wrong: ${error}`);
})
}
const handleClick2 = () => {
fetch('/login')
.then((response) => {
return response.json()
})
.then((json) => {
console.log(JSON.stringify(json));
})
}
if (error) {
return <p>{error}</p>
}
return (
<div>
<button onClick={handleClick}>データ呼び出し1</button>
<button onClick={handleClick2}>データ呼び出し2</button>
{data && (
<ul>
{data.people.map((person) => (
<Item
key={`${person.name}-${person.age}`}
name={person.name}
age={person.age}
/>
))}
</ul>
)
}
</div>
);
}
「データ呼び出し1」ボタンを押下すると、変数urlを fetchします。
変数urlのJson値は以下になります。
{
"data": {
"people" :
[
{
"name": "jimmy",
"age": 135
},
{
"name": "timmy",
"age": 13
},
{
"name": "cindy",
"age": 15
},
{
"name": "judy",
"age": 25
},
{
"name": "marry",
"age": 64
},
{
"name": "tommy",
"age": 109
}
]
}
}
しかし!handerls.jsに変数url requestに対してintercept定義しておいたため、結果は以下になります。