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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Tasuku Watanabe
March 13, 2026
Programming
510
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
95
axiosで作る:ファイルアップロード体験を改善する「Progress Toast」
tasukuwatanabe
0
580
Other Decks in Programming
See All in Programming
Swiftのレキシカルスコープ管理
kntkymt
0
220
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
170
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
3.6k
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
4.9k
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
150
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
520
RTSPクライアントを自作してみた話
simotin13
0
520
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4k
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
530
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.2k
Contextとはなにか
chiroruxx
0
290
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
130
Featured
See All Featured
sira's awesome portfolio website redesign presentation
elsirapls
0
280
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.7k
The Curse of the Amulet
leimatthew05
1
13k
Chasing Engaging Ingredients in Design
codingconduct
0
220
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
180
A Tale of Four Properties
chriscoyier
163
24k
Paper Plane (Part 1)
katiecoart
PRO
0
8.8k
Raft: Consensus for Rubyists
vanstee
141
7.5k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
How Software Deployment tools have changed in the past 20 years
geshan
0
34k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
530
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
140
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
ご清聴ありがとうございました