🧭第13章 実装を導くテスト(TODOリスト作成)
Kent Beckの「実装を導くテスト」の概念をTODOアプリの新機能追加に適用します。 新しいTODOアイテムを追加する機能を実装しながら、実装を導くテストの手法を学びます。
重複をすべて除去するまでは、まだ項目を「済」にできない。コードに重複はないが、データに重複がある。 それは、仮実装で返している部分だ。
📋TODOリスト
- • TODOリストコンポーネントの作成
- • TODOアイテムの追加機能
- • 一覧表示機能
🎯仮実装から本実装へのアプローチ
これまでは仮実装を本実装に導く方法として、単にベタ書きの値を変数に置き換える程度でした。 今回はTODOアイテムの追加という、より複雑な実装に挑戦します。
🔴1. レッド:失敗するテストを書く(TODOアイテム追加フォーム)
まず、新しいTODOアイテムを追加するためのフォームコンポーネントのテストを作成します。components/TodoForm.test.tsx
を作成しましょう。
components/TodoForm.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import TodoForm from './TodoForm'; // まだ存在しない
describe('TodoForm コンポーネントのテスト', () => {
it('入力フィールドと送信ボタンが表示されること', () => {
render(<TodoForm onSubmit={jest.fn()} />);
// テキスト入力フィールドの存在確認
expect(screen.getByRole('textbox')).toBeInTheDocument();
expect(screen.getByDisplayValue('')).toBeInTheDocument();
// 送信ボタンの存在確認
expect(screen.getByRole('button', { name: /todo を追加/i })).toBeInTheDocument();
});
});
このテストは失敗します(レッド)。TodoForm.tsx
が存在しないためです。
🟢2. グリーン:テストをパスさせる最小限のコードを書く
components/TodoForm.tsx
を作成し、 テストをパスさせる最小限のコードを書きます。
components/TodoForm.tsx
"use client";
import React from 'react';
interface TodoFormProps {
onSubmit: (text: string) => void;
}
export default function TodoForm({ onSubmit }: TodoFormProps) {
return (
<form>
<input type="text" defaultValue="" />
<button type="submit">TODO を追加</button>
</form>
);
}
これは仮実装です。フォームは表示されますが、実際の機能はまだ実装されていません。
🧪3. 実装を導くテストの追加
次に、フォームの実際の動作をテストする新しいテストを追加します。
components/TodoForm.test.tsx
it('フォーム送信時に入力値でonSubmitが呼ばれること', () => {
const mockOnSubmit = jest.fn();
render(<TodoForm onSubmit={mockOnSubmit} />);
const input = screen.getByRole('textbox');
const submitButton = screen.getByRole('button', { name: /todo を追加/i });
// テキストを入力
fireEvent.change(input, { target: { value: 'Next.js のテストを学ぶ' } });
// フォームを送信
fireEvent.click(submitButton);
// onSubmitが正しい値で呼ばれることを確認
expect(mockOnSubmit).toHaveBeenCalledWith('Next.js のテストを学ぶ');
expect(mockOnSubmit).toHaveBeenCalledTimes(1);
});
🔵4. 実装を導くステップでの本実装
これらのテストを通すために、実際の機能を実装します。
components/TodoForm.tsx
"use client";
import React, { useState } from 'react';
interface TodoFormProps {
onSubmit: (text: string) => void;
}
export default function TodoForm({ onSubmit }: TodoFormProps) {
const [inputValue, setInputValue] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (inputValue.trim()) {
onSubmit(inputValue.trim());
setInputValue(''); // 送信後にクリア
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="新しいTODOを入力..."
/>
<button type="submit">TODO を追加</button>
</form>
);
}
🔗5. TODOリストコンポーネントとの統合
次に、TodoForm
を使用するTodoList
コンポーネントのテストを作成します。
components/TodoList.test.tsx
it('フォーム送信時に新しいTODOが追加されること', () => {
render(<TodoList />);
const input = screen.getByRole('textbox');
const submitButton = screen.getByRole('button', { name: /todo を追加/i });
// 新しいTODOを追加
fireEvent.change(input, { target: { value: '新しいTODOアイテム' } });
fireEvent.click(submitButton);
// 追加されたTODOが表示されることを確認
expect(screen.getByText(/新しいTODOアイテム/i)).toBeInTheDocument();
});
💡実装を導くテストの考察
この章では、Kent Beckの「実装を導くテスト」の手法を実践しました:
- • 段階的実装: 最初は仮実装から始めて、テストに導かれながら本実装へ
- • テスト主導の設計: テストが実装の方向性を決定
- • 機能の分割: 大きな機能を小さなテスト可能な単位に分割
- • エッジケースの考慮: 実装後に追加のテストでエラーハンドリングを改善
✅TODOリストの更新
- • ✅ TODOリストコンポーネントの作成
- • ✅ TODOアイテムの追加機能
- • ✅ 一覧表示機能
🚀コミットとCIの確認
git add . git commit -m 'feat: Implement TodoForm and TodoList with TDD approach (Chapter 13)' git push
次の章では、学習用テストと回帰テストの実践を通じて、TODOアイテムの変更処理を実装します。