from Jestメモ Jestメモ Day3 --- day 3
MissingAPIError: indexedDB API missing
- Firestoreの時と同様にIndexeDBに触れるコードも分離してモックで置き換える必要がある
- 前回、Firestoreから値を取った後のコードを下記のように切り出してエクスポートし、これを直接呼び出すPromiseでgetNewTalkIDを置き換える、というモックをした ts
// exported for test
export const _gotNewTalkID = (text: string) => {
return localDB.talks
.orderBy("id")
.reverse()
.limit(1)
.toArray()
.then((x) => {
const previousTalkID = x[0]?.TalkID;
if (previousTalkID !== undefined) {
setGlobal({ TalkID: text, previousTalkID: x[0].TalkID });
} else {
setGlobal({ TalkID: text, previousTalkID: "" });
}
localDB.talks.add({ TalkID: text });
});
};
- Promiseの型を明記しつつ読み出し、利用、書き込みの3つの関数に分割
- Jestのモックは同一モジュール内の呼び出しに影響を与えないから、IndexedDBに触れるところだけ別モジュールにくくり出してモックしよう
- ユーザーモジュールのモック doc
- なるほど
- でも2通りの実装が欲しいな mocks/managePreviousTalkID.ts
// no previoud id
export const getPreviousTalkID = (): Promise<string> => {
return Promise.resolve("");
};
export const MOCK_PREVIOUS_ID_EXISTS = (): Promise<string> => {
return Promise.resolve("test");
};
export const updatePreviousTalkID = (currentTalkID: string): void => {};
- トップ画面の描画テストは動くようになった
- jest.mockをテストケースの中ではなくトップレベルに置かないと期待通りにモックされない、なぜ?
- 複数のモックの挙動を切り替えてテストしたいのだが…
-
jest.mockをテストケースの中ではなくトップレベルに置かないと期待通りにモックされない、なぜ?
-
docs: ES module importsを使用している場合、通常はテストファイルの先頭でimport宣言を書くことが多いでしょう。 しかしモジュールがそれらを使用するのに先立ち、Jestにモックを使用するよう指示する必要があります。 このため、Jestは自動的にjest.mockコールを自動的にモジュールの先頭に(importを行う前に)移動します。
- 「先におかないといけないのでは?」と思ったがESLintが “Import in body of module; reorder to top.” と文句を言うので後に置いてた。
- 後においても動くのでmockのタイミングでインポート済みのものを置き換えるのかなと思ってたがそういうことではないようだ。
-
- spyOnなら確実にそのタイミングでの差し替えを行うので、重ねてspyOnすることにしてみた
- previousTalkIDが存在するかどうかでメニューの表示が異なる、というテストが一応動いた
- しかし(1)の部分、メニューが実際に存在しないのか、まだ非同期の再描画が終わってないのか判断がつかないと思う
- タイムアウトするまで待てば判断つくけど、テストが遅くなるから嫌だし ts
const { container } = render(<App />);
expect(container).toMatchSnapshot();
fireEvent.click(screen.getByLabelText("menu"));
expect(screen.queryByText("Re-enter to Last Talk")).toBeNull(); // (1)
jest
.spyOn(managePreviousTalkIDModule, "getPreviousTalkID")
.mockResolvedValue("test");
render(<App />);
await waitFor(() => screen.getByText("Re-enter to Last Talk"));
expect(screen.queryByText("Re-enter to Last Talk")).not.toBeNull();
Reactのテストでactで包むのはrenderではなく状態更新 Promiseの結果で状態更新する場合、全体をactで包んでもダメ
次回「useStateをモックで置き換えたらいいのでは?」