LESSON 30分

テストコード生成とリファクタリング

ストーリー

「テスト書くの、正直めんどくさいよな」

中島先輩がいたずらっぽく笑った。

「え、先輩もそう思うんですか?」

「思うよ(笑)。でもテストは品質を守る命綱だ。だからこそ、 めんどくさい部分は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分