Open70

『フロントエンド開発テスト入門』読みつつキャッチアップ

tkrytkry

第1章と第2章は読み直したのでスキップ
社内でのテストを考える上ではまた読み返した方がいいかも

tkrytkry
tkrytkry

グローバルで書き変わる感じ、ちょっと気持ち悪い(というか認知負荷が発生する)

tkrytkry

第5章 UIコンポーネントテスト

セットアップとしてjsdomやtesting-libraryを導入しているが、storybookがあれば不要になったはず
testing-libraryが同伴されてるし、vitestがデフォルトになったから(たぶん)

https://github.com/tsuka-ryu/my-unittest/tree/main/src/05

tkrytkry

うーん、Form.test.tsxを作成してBrowser modeでテストさせようとすると、vitest.config.jsをどうやって書けばいいのかよくわからない

*.test.tsxのみを対象にBrowser modeテストを書く?

tkrytkry

snapshotテストはあんまり意味ない気がするからVRTに寄せたい(けど後で出てくるからいいや)

tkrytkry

play functionにテスト書き始めるとstoriesファイルがごちゃつく気がするな
まあ仕方ない(?)

NoItemとかの表示とテストが紐づいてるのはわかりやすくていい気もする

tkrytkry

fetch関数をmockしようと思ったけど、mswじゃないとうまくmockできない?
vitest単体のモックとstorybookがからんだ時のモックの違いがよくわからんなぁ

tkrytkry

いや全てのStoryで特定の関数がモックされるの辛いな、これはダメだ
(そういうシチュエーションはあるかもだけど今回はちょっと違った)

tkrytkry

ひとまず、対象の関数はモックして、その処理に近い形でエラーハンドリングを扱えるようにした
なぜかValidationErrorのインスタンスはstoryの中で扱えなかった(ESMがどうの、みたいな話っぽい?よくわからん)

tkrytkry

browser modeはスクショをとってるからか遅い気がする
あと、スクショってコミットしないんだっけ?

tkrytkry

第6章 カバレッジレポートの読み方

https://vitest.dev/guide/coverage.html#custom-coverage-provider

tkrytkry
  • Stmts(命令網羅率)
    • 全てのステートメントが少なくとも1回実行されたか
  • Branch(分岐網羅率)
    • 全ての分岐条件が少なくとも1回通過したか
  • Funcs(関数網羅率)
    • 全ての関数が少なくとも1回呼び出されたか
    • exportされてるが使われてない、など(knipでもいいけど)
  • Lines(行網羅率)
    • 全ての行を少なくとも1回通過したか
tkrytkry

カバレッジ率、すべて100%以外だと割れ窓になる気がするけど実際は複雑な理由で難しいんだろうな

tkrytkry

6章までの感想

ユニットテストの章が終わったので感想

  • 以前は環境構築の大変さで心折れてた気がするけどStorybookやVitestのおかげで全然楽
    • jestとかjsdomとかhappydomとか、そこらへんでつらみがないのがよい
  • 困ったらClaude Codeに聞けばほぼ解決できる
    • モック周りは全然ダメだったけど...
  • モック周りはStorybookだと小回りきかない気もするけど、別にmswや今の機能でそんなに困らないかな?
    • ストーリーごとに細かくモック制御したいけどビルド時に何やら解決してるから無理っぽい
    • その点、vitestで書いて、browser modeとかの方が今までの書き方できて楽ではある
  • Browser Modeでテストは安心だけど、ちょっと遅い(ひとつのコンポーネントテストで数秒かかった)
    • 素のまま使ってたらスクショとってくれてたけどそのせいかな、設定でスクショ抑制する?
  • storiesファイルがごちゃつくのはどうなんだろう
    • 単純にカタログとしてみたい時、play functionが先に動くとちょっと邪魔な気もする
    • スモークテストとして壊れてないことが確認できるからいいんだろうけど...何か方法あるのかな
tkrytkry

Agentのおかげでテスト書くコストはほぼなくなった気がする
ちゃんとパターンのレビューさえ出来ればかなり楽できる
(今回はRuleとか書かずに雑に指示してたけど、それでもまあまあよかった)

tkrytkry

7章以降のための環境構築

App routerとか使いたいので自前で環境作り直す
気まぐれでパッケージマネージャーはbun、あとbiomeの組み合わせ

// nextjs
bun create next-app@latest . --yes
// 動作確認
bun dev
// biome導入
bun add --dev --exact @biomejs/biome
// vitest browser mode
bunx vitest init browser
tkrytkry

storybookは8章で扱いそうなのでいったんbrowser modedだけでやる

tkrytkry

7章 Webアプリケーションの結合テスト

ひとまずはコピペしたコンポーネントに対してテストを書く(というかClaude Codeに書いてもらう)

困っても雑にCalude Codeに聞くと解決してくれてうれしい、テストのつらみがだいぶ減ってる

tkrytkry

import Link from 'next/link'ってモックしなくていいの?普通にテストとして書いてあるな
けどリポジトリにこのコードはなさそう

tkrytkry

手元で失敗したから聞いたらclaude codeはこういうコードを吐いた
丸っと書き換えちゃってるからダメな気がする?

あとnext/linkのせいでcoverageのbranchが50%になってしまう

// Mock Next.js Link component
vi.mock("next/link", () => {
  const MockedLink = ({
    children,
    href,
    ...props
  }: {
    children: React.ReactNode;
    href: string;
    [key: string]: unknown;
  }) => (
    <a {...props} href={href}>
      {children}
    </a>
  );
  return {
    __esModule: true,
    default: MockedLink,
  };
});
tkrytkry

vitestでnext/routerとnext/linkをいい感じにモックするにはどうしたらいいんだ

tkrytkry
⏺ Next.jsのルーターをモックするには、next/navigationのuseRouterをモック
  する方法が一般的です。

  import { vi } from "vitest";

  // Next.js navigation hooks をモック
  const mockPush = vi.fn();
  const mockReplace = vi.fn();

  vi.mock("next/navigation", () => ({
    useRouter: () => ({
      push: mockPush,
      replace: mockReplace,
      pathname: "/",
      query: {},
    }),
  }));

  ただし、Linkコンポーネントのクリックテストの場合は、実際にナビゲーシ
  ョンが発生するかではなく、正しいhrefが設定されているかをテストする方
  が実用的です:

  test("リンクが正しいURLを指している", async () => {
    const screen = render(<Heading />);
    const link = screen.getByRole("link");
    await expect.element(link).toHaveAttribute("href", "/");
  });

  どちらのアプローチがお好みですか?
tkrytkry

https://github.com/frontend-testing-book/nextjs/blob/c61afa5665e2d396d9ddc34ea8bffa5bd22cd9a0/src/components/layouts/BasicLayout/Header/LoginUser/index.test.tsx

(テストの内容を理解してないけど)
このテストを実装しようとしたら山ほどモックするものがあって大変、全部Agentにやってもらってるけど

tkrytkry

ブラウザモードではwindow.location.reloadのモックが制限されています

これのせいでうまくテストができない、そもそもテストのやり方が良くない?

tkrytkry

こういうのはE2Eテストでいいかもしれない

tkrytkry

ほっといたらわけわからんクソデカ差分になり辛み
しかもテスト通せないので、skip

tkrytkry

Headerのテストは後で見返した方が良さそうやな

tkrytkry

import { composeStories } from "@storybook/testing-react";を使ってるテストが11件ある、それは後でいいか

tkrytkry

テストケースみてるとplay functionで十分な気がする、composeStories使う必要はもうない気がする