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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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