Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
モックわからないマン卒業記 ~振る舞いを起点に見直した、フロントエンドテストにおけるモックの使...
Search
Tasuku Watanabe
March 13, 2026
Programming
560
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
モックわからないマン卒業記 ~振る舞いを起点に見直した、フロントエンドテストにおけるモックの使いどころ~
Tasuku Watanabe
March 13, 2026
More Decks by Tasuku Watanabe
See All by Tasuku Watanabe
useImperativeHandleで理解する クロージャと評価タイミング
tasukuwatanabe
1
110
axiosで作る:ファイルアップロード体験を改善する「Progress Toast」
tasukuwatanabe
0
610
Other Decks in Programming
See All in Programming
Go 1.27 における memory allocation の高速化
andpad
0
250
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
780
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
480
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
5
1.9k
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
530
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
270
SlackアプリとLambdaの 連携を構築した話
pawn_4_s
1
120
React本体のコードリーディング
high_g_engineer
1
150
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
130
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
430
いまどきの Codex で開発する visionOS アプリの開発スタイルについて
karad
0
150
170k Jobs a Day on GKE: Scaling Mercari's CI Platform - and What's Next for AI-Native Development
junyaokabe
0
110
Featured
See All Featured
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
260
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
240
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
64
56k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
How to build a perfect <img>
jonoalderson
1
5.9k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.8k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.5k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
490
Darren the Foodie - Storyboard
khoart
PRO
3
3.7k
Transcript
モックわからないマン卒業記 振る舞いを起点に見直した、フロントエンドテストにおけるモックの使いどころ HRBrain 渡邉佑 2026-03-13 React Tokyo #14
自己紹介 渡邉 佑 ・HRBrain ・新潟県佐渡島出身 ・新卒で航海士→エンジニア ・PlaywrightでE2Eテスト実装中 ・X: @tasuku_web 2
フロントでテストを書く デグレを発生させず安心して機能開発したい AIでたくさんリファクタリングしたい。 → Vitest + React Testing Library でテストを書いている。
import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Counter } from "./Counter"; test("ボタンをクリックするとカウントが増える", async () => { render(<Counter />); await userEvent.click(screen.getByRole("button", { name: "増やす" })); expect(screen.getByText("1")).toBeInTheDocument(); }); 3
モックという概念が登場 テストを書こうとすると、コンポーネント内部に外部の依存が潜んでいる。 function UserGreeting() { // propsや引数ではなく、コンポーネントが内部で直接参照している外部の値(= 外部依存) const {
data } = useFetchUser(); // API通信 const now = useCurrentTime(); // 現在時刻 const { userId } = useAuth(); // 認証状態 } 4
モックを使わないと何が起きるか テスト対象のコンポーネント APIを内部で呼び出しているコンポーネント export function UserGreeting() { // API通信が内部で走る const
{ data } = useFetchUser(); if (!data) return <p>読み込み中...</p>; return <p>こんにちは、{data.name} さん</p>; } モックなし:テストが書けない ・ネットワーク環境に依存する ・テストが遅い・不安定 ・エラー系など特定ケースの再現が難しい test("ユーザー名が表示される", async () => { render(<UserGreeting />); // 実際のAPIが走る → ネットワーク環境がなければ失敗する expect(await screen.findByText("こんにちは、Alice さん") }); 5
モックで依存を差し替えてテスト可能 ・ネットワーク不要・高速 ・テストしたいケースを自由に再現できる ・外部サービスの状態に左右されない import * as hooks from "./useFetchUser";
test("ユーザー名が表示される", async () => { vi.spyOn(hooks, "useFetchUser").mockReturnValue({ data: { name: "田中" }, }); render(<UserGreeting />); expect(screen.getByText("こんにちは、田中 さん")).toBeInTheDocument(); }); 6
実際のテストファイルではこうなりがち // ① モジュールモック vi.mock("./useRouter"); vi.mock("./useAuth"); // ② HTTPモック(MSW) const
server = setupServer( http.get("/api/users", () => HttpResponse.json([{ name: "田中" }])), ); beforeAll(() => server.listen()); afterAll(() => server.close()); beforeEach(() => { vi.useFakeTimers(); // ③ タイマーモック vi.setSystemTime(new Date("2026-01-01")); vi.mocked(useRouter).mockReturnValue({ push: vi.fn() }); vi.mocked(useAuth).mockReturnValue({ userId: "u1" }); }); test("ユーザー一覧が表示される", async () => { const onSelect = vi.fn(); // ④ モック関数 render(<UserListPage onSelect={onSelect} />); expect(await screen.findByText("田中")).toBeInTheDocument(); }); 7
「APIが呼ばれたこと」を検証するのはNG ❌ よくある検証 import { fetchUsers } from "./api"; vi.mock("./api");
test("ユーザー一覧を取得する", () => { render(<UserListPage />); // APIが呼ばれたことを確認 expect(fetchUsers).toHaveBeenCalledWith("/api/users"); }); なぜNGか ・URLが /api/v2/users に変わるだけで壊れる ・画面が正しく表示されていてもテストが失敗する = リファクタリングのたびにテストも壊れる 8
だから、振る舞いをテストする
「振る舞いをテストする」という考え方 確認すべきは「何が呼ばれたか」より「画面がどう振る舞うか」 // 実装を見る(useStateの更新を直接確認) expect(setCount).toHaveBeenCalledWith(1); // 振る舞いを見る(画面がどう変わるかを確認) expect(screen.getByText("1")).toBeInTheDocument(); ・内部実装を変えても壊れにくい( setCount
→ useReducer に変えてもテストはパスする) ・ユーザーの視点でテストを書ける(ユーザーが気にするのは画面の動作であり、内部実装ではない) ・テストが仕様書になる( 「この操作をするとこう表示される」という意図が明確になる) 10
「振る舞いをテストする」という考え方 API通信: 「fetchが呼ばれたか」より「結果が表示されるか」 // 実装を見る(fetchの呼び出しを直接確認) expect(mockFetch).toHaveBeenCalledWith("/api/users"); // 振る舞いを見る(取得したデータが画面に表示されるかを確認) expect(screen.getByText("田中太郎")).toBeInTheDocument(); ・
実装依存: fetch の呼び出し先URL が変わるだけでテストが壊れる。コンポーネントの「結果」 は正しくても失敗する ・ 振る舞いベース:URLが変わっても、別のライブラリに乗り換えても、画面に結果が出ればテスト はパスする 11
「振る舞いをテストする」という考え方 ルーティング: 「router.pushが呼ばれたか」より「画面が遷移したか」 // 実装を見る(router.pushの呼び出しを直接確認) expect(mockPush).toHaveBeenCalledWith("/dashboard"); // 振る舞いを見る(遷移後のページが表示されるかを確認) expect( screen.getByRole("heading",
{ name: "ダッシュボード" }), ).toBeInTheDocument(); 12
まとめ モックで外部依存を差し替える → テストが書けるようになる API通信・認証・時刻などを固定してテスト可能に でも多用すると、リファクタリング耐性が下がる 実装詳細(関数名・モジュール構造)に依存し、テストが壊れやすくなる だから、振る舞い(画面の動作)をテストする 「何が呼ばれたか」より「画面がどう変わるか」を確認する 13
ご清聴ありがとうございました