テストコード生成とリファクタリング
ストーリー
「テスト書くの、正直めんどくさいよな」
中島先輩がいたずらっぽく笑った。
「え、先輩もそう思うんですか?」
「思うよ(笑)。でもテストは品質を守る命綱だ。だからこそ、 めんどくさい部分はCopilotに任せて、テスト設計に集中するのが正解なんだ」
「テストの骨格はAIに書かせて、テスト観点は自分で考えるってことですか」
「そういうことだ。リファクタリングも同じ発想でいける」
Copilotでテストを生成する
基本: /tests コマンド
手順:
1. テストしたい関数を選択
2. Copilot Chat で "/tests" と入力
3. 生成されたテストを確認・修正
テスト生成の実践例
テスト対象の関数:
typescript
const validatePassword = (password: string): ValidationResult => {
const errors: string[] = [];
if (password.length < 8) {
errors.push('パスワードは8文字以上必要です');
}
if (!/[A-Z]/.test(password)) {
errors.push('大文字を1文字以上含めてください');
}
if (!/[a-z]/.test(password)) {
errors.push('小文字を1文字以上含めてください');
}
if (!/[0-9]/.test(password)) {
errors.push('数字を1文字以上含めてください');
}
return { isValid: errors.length === 0, errors };
};Copilotが生成するテスト:
typescript
describe('validatePassword', () => {
it('should pass for valid password', () => {
const result = validatePassword('StrongPass1');
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should fail for short password', () => {
const result = validatePassword('Abc1');
expect(result.isValid).toBe(false);
expect(result.errors).toContain('パスワードは8文字以上必要です');
});
it('should fail without uppercase', () => {
const result = validatePassword('lowercase1');
expect(result.isValid).toBe(false);
expect(result.errors).toContain('大文字を1文字以上含めてください');
});
// ... 他のケースも自動生成
});AIが生成したテストへの追加が必要なケース
Copilotが見落としがちなテストケース:
- 空文字列の入力
- 非常に長い入力(10,000文字等)
- マルチバイト文字(日本語、絵文字)
- null/undefined の入力(TypeScript strict の場合は不要)
- 境界値(ちょうど8文字のパスワード等)
→ AIが生成したテストに自分でエッジケースを追加する
Copilotでリファクタリング
インラインChatでリファクタリング依頼
手順:
1. リファクタリングしたいコードを選択
2. Ctrl+I でインラインChatを開く
3. 具体的なリファクタリング指示を入力
実践例: ネストの深いコードを改善
Before:
typescript
const processOrder = (order: Order): Result => {
if (order) {
if (order.items.length > 0) {
if (order.status === 'pending') {
const total = order.items.reduce((sum, item) => {
if (item.price > 0 && item.quantity > 0) {
return sum + item.price * item.quantity;
}
return sum;
}, 0);
if (total > 0) {
return { success: true, total };
}
}
}
}
return { success: false, total: 0 };
};Copilotへの指示:
このコードを早期リターンパターンでリファクタリングしてください。
ネストを2段階以内に減らしてください。
After(Copilotの提案):
typescript
const processOrder = (order: Order): Result => {
if (!order || order.items.length === 0) {
return { success: false, total: 0 };
}
if (order.status !== 'pending') {
return { success: false, total: 0 };
}
const total = order.items
.filter(item => item.price > 0 && item.quantity > 0)
.reduce((sum, item) => sum + item.price * item.quantity, 0);
if (total <= 0) {
return { success: false, total: 0 };
}
return { success: true, total };
};よくあるリファクタリングの指示例
| 指示 | 効果 |
|---|---|
| 「早期リターンに書き換えて」 | ネスト削減 |
| 「関数を分割して」 | 単一責任の原則 |
| 「マジックナンバーを定数に」 | 可読性向上 |
| 「繰り返しを共通関数に」 | DRY原則 |
| 「async/awaitに変換して」 | モダンな非同期処理 |
| 「型を厳密にして(any 排除)」 | 型安全性向上 |
テスト→リファクタリングの安全なサイクル
安全なリファクタリング手順:
1. まずテストを生成(/tests)
→ 現在の動作を保証するテストを確保
2. テストを実行して全て通ることを確認
→ 出発点が正しいことを証明
3. Copilotにリファクタリングを依頼
→ コードを改善
4. テストを再実行
→ リファクタリングで動作が変わっていないことを確認
5. 必要に応じてテストも更新
→ 新しいインターフェースに合わせる
まとめ
| ポイント | 内容 |
|---|---|
| テスト生成 | /tests で骨格を生成し、エッジケースは自分で追加 |
| リファクタリング | インラインChatで具体的な改善指示を出す |
| 安全なサイクル | テスト確保 → リファクタリング → テスト再実行 |
| AI生成テストの限界 | エッジケースや境界値テストは人間が補完 |
チェックリスト
- /tests でテストを自動生成できた
- AIが見落としがちなテストケースを理解した
- インラインChatでリファクタリングを実行できた
- テスト→リファクタリング→テストのサイクルを理解した
次のステップへ
テスト生成とリファクタリングの技術を身につけたら、次は演習: Copilotでアプリを構築します。
推定読了時間: 30分