Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AIのためのテスト戦略 〜TDDが難しいフロントエンド開発でのアプローチ〜

AIのためのテスト戦略 〜TDDが難しいフロントエンド開発でのアプローチ〜

Avatar for ディップ株式会社

ディップ株式会社 PRO

October 06, 2025
Tweet

More Decks by ディップ株式会社

Other Decks in Technology

Transcript

  1. チーム状況 🧰 技術スタック TypeScript, React Router v7 🧠 AIコーディング JiraやFigmaをMCP経由で

    Claude Code に取り込み、実装させる 󰞵 チーム人数 エンジニア3人 少人数ながら AIを活用してスピードと品質の両面を追う
  2. it("'Hello World' のテキストが表示されること ", () => { render(<Greeting text={"Hello World"}

    />); expect(screen.getByText("Hello World")).toBeInTheDocument(); }); export function Greeting({ text }: GreetingProps) { return <p>{text}</p>; } Propsで渡されたテキストを表示するコンポーネントに対して、 テキストが表示されること ➡ 効果の低いテストを過剰に書いてしまう
  3. ➡ ユーザー操作を含むテストは再現するのが難しく、コストが高い // テスト上でスクロール操作ができないので、スクロール量を上書きする const setScrollY = (y: number) =>

    { Object.defineProperty(window, "scrollY", { configurable: true, value: y, }); window.dispatchEvent(new Event("scroll")); }; it("スクロール量が閾値を超えたらナビゲーションバーが表示される", () => { render(<Navbar />); expect(screen.queryByTestId("navbar")).toBeNull(); setScrollY(120); expect(screen.getByTestId("navbar")).toBeInTheDocument(); });
  4. 📚 composeStories・・・ Storybookのストーリーをテスト環境で利用できる仕組み const meta: Meta<typeof Button> = { title:

    "Components/Button", component: Button, args: { label: "Click Me" }, }; export default meta; type Story = StoryObj<typeof Button>; export const Primary: Story = { args: { disabled: false }, }; export const Disabled: Story = { args: { disabled: true, label: "Disabled" }, }; it("Primary: ~~~~", async () => { render(<Primary onClick={onClick} />); // 略 expect(onClick).toHaveBeenCalledTimes(1); }); it("Disabled: ~~~~", async () => { render(<Disabled onClick={onClick} />); // 略 expect(onClick).not.toHaveBeenCalled(); }); Storybook テスト
  5. 📚 composeStories・・・ Storybookのストーリーをテスト環境で利用できる仕組み const meta: Meta<typeof Button> = { title:

    "Components/Button", component: Button, args: { label: "Click Me" }, }; export default meta; type Story = StoryObj<typeof Button>; export const Primary: Story = { args: { disabled: false }, }; export const Disabled: Story = { args: { disabled: true, label: "Disabled" }, }; it("Primary: ~~~~", async () => { render(<Primary onClick={onClick} />); // 略 expect(onClick).toHaveBeenCalledTimes(1); }); it("Disabled: ~~~~", async () => { render(<Disabled onClick={onClick} />); // 略 expect(onClick).not.toHaveBeenCalled(); }); Storybook テスト
  6. Arrange(準備), Act(実行), Assert(検証)の 3つセクションに明示的に分けた構成でテストを書く it('2つの正の数を正しく足し算できること', () => { // Arrange

    (準備): const num1 = 5; const num2 = 3; const expectedSum = 8; // Act (実行): const actualSum = add(num1, num2); // Assert (検証): expect(actualSum).toBe(expectedSum); });
  7. it('応募ボタンを押すと確認画面に進むこと ', () => { ... }); ✅ Good ❌

    Bad it('onSubmit が navigate('/confirm') を呼ぶこと', () => { ... }); ユーザーの行動と成果が主語になるように書く
  8. テストファイルは__tests__/とかtests/ に置くのではなく テスト対象のコードの同じディレクトリ配下に置く route/ └── top/ ├── route.tsx └── top.test.tsx

    ⬅ util/ └── date/ ├── date.test └── date.test.tsx ⬅ tests/ ├── route/ │ ├── top.test.tsx │ └── job-detail.test.tsx └── test/ ├── top.test.tsx └── job-detail.test.tsx ✅ Good ❌ Bad