React Testing Libraryを使ってみた(fetch編)
はじめに
今回はReact Testing Libraryでfetchをテストしてみます。
テスト
テスト対象コンポーネント
今回のテスト対象のコンポーネントです。
ボタンを押すとpropsで渡されたURLにfetchし、その内容を表示します。
import React from "react";
const Fetch = (props) => {
const [json, setJson] = React.useState("");
const handleClick = async () => {
fetch(props.url)
.then(response => response.json())
.then((responseJson) => {
setJson(JSON.stringify(responseJson));
});
};
return (
<div>
<button onClick={handleClick}>button</button>
{json}
</div>
);
};
export default Fetch;
テストコード
import React from "react";
import { render, screen, cleanup } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { rest } from "msw";
import { setupServer } from "msw/node";
import Fetch from "./Fetch";
const server = setupServer(
rest.get("http://example.com/somethings/1", (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ data: "something data" }));
})
);
beforeAll(() => server.listen());
afterEach(() => {
server.resetHandlers();
cleanup();
});
afterAll(() => server.close());
describe("HTTPリクエスト", () => {
it("【データ取得成功】[server]で設定したレスポンスが返ってくる事", async () => {
render(<Fetch url={"http://example.com/somethings/1"} />);
userEvent.click(screen.getByRole("button"));
expect(await screen.findByText("{\"data\":\"something data\"}")).toBeInTheDocument();
});
it("【データ取得失敗】[server]で設定したレスポンスが返って来ない事", async () => {
// テストケース内だけで使用するAPIのエンドポイントを設定する。
server.use(
rest.get("http://example.com/somethings/1", (req, res, ctx) => {
return res(ctx.status(404));
}
)
);
render(<Fetch url={"http://example.com/somethings/1"} />);
userEvent.click(screen.getByRole("button"));
expect(screen.queryByText("something title")).toBeNull();
});
});
テストで使用するAPIのエンドポイントを設定しています。
const server = setupServer(
rest.get("http://example.com/somethings/1", (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ data: "something data" }));
})
);
beforeAllを使用すると、最初の1回だけ実行したい処理を記述できます。
ここでは上記で記述したserverを起動しています。
beforeAll(() => server.listen());
afterEachを使用すると、テストケース毎にテスト後に実行したい処理を記述できます。
server.resetHandlersはserverを毎回リセットします。
cleanupはrenderでマウントしたコンポーネントをアンマウントします。
今回の様なシンプルなケースでは無くても問題ありませんが、基本的には書いておく方が良いと思います。
afterEach(() => {
server.resetHandlers();
cleanup();
});
afterAllを使用すると、最後の1回だけ実行したい処理を記述できます。
serverを停止しています。
afterAll(() => server.close());
「await screen.findByText」を使用すると、fetchのレスポンスが返ってくるまで待つ事ができます。
expect(await screen.findByText("{\"data\":\"something data\"}")).toBeInTheDocument();
server.useを使用すると、テストケース内だけで使用するAPIのエンドポイントを設定できます。
server.use(
rest.get("http://example.com/somethings/1", (req, res, ctx) => {
return res(ctx.status(404));
}
)
);
APIのレスポンスが404を返すので、期待した値が表示されない事をテストしています。
expect(screen.queryByText("something data")).toBeNull();
補足
上記のテストコードだと処理時間が長い場合はエラーになってしまいます。
ここでは処理時間が長いfetchのテストについて記述します。
テスト対象コンポーネント
先程とほぼ同じですが、重い処理を想定して3秒待つ処理を入れています。
import React from "react";
const HeavyFetch = (props) => {
const [json, setJson] = React.useState("");
const handleClick = async () => {
await new Promise(resolve => setTimeout(resolve, 3000)); // 重い処理を想定
fetch(props.url)
.then(response => response.json())
.then((responseJson) => {
setJson(JSON.stringify(responseJson));
});
};
return (
<div>
<button onClick={handleClick}>button</button>
{json}
</div>
);
};
export default HeavyFetch;
テストコード
前回のテストコードで実行すると、以下でエラーになると思います。
awaitで待つ時間が1秒なので、まだレスポンスが帰ってきていないのが原因です。
waitForで待機する事でタイムアウトを回避できます。
await waitFor(async () => {
const result = await screen.findByText("{\"data\":\"something data\"}");
expect(result).toBeInTheDocument();
}, {timeout: 5000});
さらに処理時間が長い場合はjestがタイムアウトするので、以下でタイムアウトを伸ばす事ができます。
jest.setTimeout(10000);
ディスカッション
コメント一覧
まだ、コメントがありません