5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HITOTSU株式会社Advent Calendar 2022

Day 4

【フロントテスト】ReactでのMSWの導入

Last updated at Posted at 2022-12-04

はじめに

はじめまして、河村康治です。HITOTSUアドベントカレンダーの4日目担当します!!
普段はバックエンドを主にやっているのですが、E2Eテストの知見を深めていきたいなと思っています。なので、今回のカレンダーではE2Eのフロントエンド周りを中心に投稿していきます!!
今回の記事では、Mockサーバを利用したフロントエンドのテストです。バックエンド開発中にフロントエンドのテストが進められるなど、何かと便利なMSWを紹介します!!

やりたい事

通常時はAPIサーバと連動する。テスト時のみServiceWorkerが起動し固定値が返却される。
これにより、APIサーバに依存する事なく、API連動時のフロントの挙動を確認する事ができるようになります。

image.png

MSWとは

ServiceWorkerを利用したMockサーバです。ネットワーク層でリクエストをインターセプトし値を返却します。そのため、アプリケーションの挙動はそのままで、テストや開発、デバッグで利用されているライブラリです。
スクリーンショット 2022-11-06 18.24.39.png

Hands-onスタート

環境情報

## typescriptでプロジェクト作成
yarn create react-app msw_test2 --template typescript

## mswのインストール
~/develop/react$ yarn add --dev msw

## mockServiceWorke.jsをコマンドで作成
npx msw init public/ --save

## mswとは関係ないが、Hands-onで使うライブラリ
yarn add @tanstack/react-query
yarn add axios
ディレクトリ構成
~/develop/react/msw_test$ tree -I node_modules 
.
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   ├── mockServiceWorker.js
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── api
│   │   └── login.ts
│   ├── components
│   │   └── Login.tsx
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── mocks
│   │   ├── browser.ts
│   │   └── handler.ts
│   ├── react-app-env.d.ts
│   ├── reportWebVitals.ts
│   └── setupTests.ts
├── tsconfig.json
└── yarn.lock

5 directories, 24 files
package.json
{
  "name": "msw_test",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@tanstack/react-query": "^4.14.3",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "axios": "^1.1.3",
    "msw": "^0.47.4",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.4.2",
    "web-vitals": "^2.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "msw": {
    "workerDirectory": "public"
  }
}
env
// 今回は、REACT_APP_ENVがtestの時のみmswが起動するように設定。
REACT_APP_ENV=test

ソースコード

mockの設定

src/mocks/browser.ts
import {setupWorker} from 'msw';
import {handlers} from './handler';

// 指定されたリクエストハンドラを持つサービスワーカーを設定する
export const worker = setupWorker(...handlers);
src/mocks/handler.ts
import {rest} from 'msw';

export const handlers = [
  rest.get('https://jsonplaceholder.typicode.com/todos/1', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({
        userId: 2,
        id: 2,
        title: 'Mockですよ!!!',
        completed: false,
      })
    );
  }),
];

アプリケーション側

index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';

const queryClient = new QueryClient();

// テスト環境の時のみ、ServiceWorkerを登録する
if (process.env.REACT_APP_ENV === 'test') {
  const {worker} = require('./mocks/browser');
  worker.start();
}

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

src/App.tsx
import React from 'react';
import {Login} from './components/Login';

function App() {
  return <Login />;
}

export default App;
src/components/Login.tsx
import {useFetchAPI} from '../api/login';

export const Login = () => {
  const {isLoading, isError, data} = useFetchAPI();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (isError) {
    return <span>Error...</span>;
  }

  return (
    <div>
      ユーザID:{data.userId}
      <br />
      ID:{data.id}
      <br />
      タイトル:{data.title}
      <br />
      完了:{data.completed ? '完了' : '未完了'}
      <br />
    </div>
  );
};
src/api/login.ts
import axios from 'axios';
import {useQuery} from '@tanstack/react-query';

export const getAPI = async () => {
  const baseUrl = 'https://jsonplaceholder.typicode.com/todos/1';
  const {data} = await axios.get<User>(baseUrl);
  return data;
};

export const useFetchAPI = () => {
  return useQuery(['test'], () => getAPI());
};

export type User = {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
};

動作確認

今回は、JSONPlaceholderを利用しています。
通常はJSONPlaceholderを実行し、テスト環境の時はServiceWorkerを実行する事になります。

■通常時
スクリーンショット 2022-11-06 18.51.37.png

■テスト時
スクリーンショット 2022-11-06 18.51.18.png

これが出ていればOKです!!!
ちなみに、MSWが起動している時は、Console画面に「[MSW] Mocking enabled.」と表示されます。

最後に

今回はMSWの導入について記載しました。次回移行の記事では、JestやPlayWrightでのMSW導入の話に繋げていきたいと思います!!

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?