AIコーディング時代におけるテストの役割

AIコーディング時代におけるテストの役割

AIコーディングアシスタントを開発に活用することで、コード作成にかかるコストは以前より大幅に下がりました。要件を説明すれば実装コードを素早く生成でき、同じ方法で実装を検証するためのテストコードも併せて作成できます。

私自身もAIコーディングを使う際、検証用テストを積極的に活用してきました。AIが作成したコードをそのまま成果物として受け入れるのではなく、要件を反映したテストも併せて作成し、それを実行して実装結果を改めて確認する方法です。実装コードだけでなくテストコードの作成コストまで下がったことで、テストを追加する負担も以前より軽くなりました。

そのような中、AI開発Workflowを支援するPluginを使いながら、TDD(Test-Driven Development)を改めて見直すようになりました。私が使ったPluginはSuperpowersで、機能実装にTDDを強く適用していました。テストコード自体には慣れていましたが、TDDを実際の開発手法として適用した経験はありませんでした。以前は実装後の検証のためにテストを書いていましたが、TDDではテストが開発プロセスの前段に置かれます。この違いを整理するうちに、「フロントエンドではTDDをどのように適用するのか?」、そして「AIコーディングアシスタントとTDDはなぜ相性がよいのか?」という疑問へと関心が広がりました。

1. テストコードを書くこととTDDで開発することは異なります

私が以前から慣れていたテスト作成の順序は、概ね実装が先でした。要件を反映してコードを書いた後、そのコードが意図どおりに動作するかをテストで確認しました。AIコーディングを使う場合も、この基本的な順序は同じでした。

요구사항 확인
    ↓
코드 구현
    ↓
테스트 코드 작성
    ↓
구현 결과 검증

TDDではこの順序を入れ替えます。まず、まだ実装されていない要件をテストとして表現します。テストが失敗している状態がREDです。その後、現在失敗しているテストを通過させる最小限のコードを書いてGREENにし、テストが通過し続ける状態で構造を整理するREFACTORを行います。

요구사항
    ↓
RED      : 실패하는 테스트 작성
    ↓
GREEN    : 테스트를 통과시키는 최소 구현
    ↓
REFACTOR : 동작을 유지하며 코드 정리
    ↓
다음 요구사항으로 반복

例えば、「注文金額が10,000ウォン以上なら送料が無料になる」という要件がある場合、送料計算ロジックより先にテストを書くことができます。

@Test
void 만원_이상_주문하면_배송비는_무료이다() {
    int deliveryFee = calculateDeliveryFee(10_000);

    assertThat(deliveryFee).isEqualTo(0);
}

まだcalculateDeliveryFeeが存在しなければ、テストは失敗します。その後、このテストを満たす最小限の実装を作り、次の要件を再びテストとして追加します。重要な違いはテストコードの有無ではなく、テストが開発プロセスのどこに位置付けられているかです。

2. テストが検証ツールから実行可能な要件へと移行します

実装後に作成するテストでは、すでに作られたコードが基準になります。一方、TDDでは要件を先にテストとして具体化し、実装コードがそのテストに従います。

구현 후 검증 : 구현 → 테스트 → 결과 확인
TDD          : 요구사항 → 테스트 → 구현 → 결과 확인

例えばログイン機能であれば、「メールアドレスを入力せずに送信すると必須入力メッセージを表示する」「ログインリクエスト中はボタンを無効にする」「ログインに成功するとメイン画面へ遷移する」といった動作を、一つずつテストとして表現できます。

このように作成されたテスト一覧は、機能が満たすべき小さな要件の一覧になります。テストが現在の実装を説明するドキュメントではなく、これから実装すべき動作を表す実行可能な仕様になるのです。

3. では、フロントエンドでは何をテストするのでしょうか?

TDDの流れを整理した後、最初に浮かんだ疑問はフロントエンドについてでした。バックエンドでは、メソッドの戻り値や例外の発生有無、状態の変化など、入力と出力が比較的明確です。一方、フロントエンドでは、ユーザーが画面上で何を見て、どのような操作を行えるかが重要なテスト対象になります。

처음 렌더링했을 때 무엇이 보이는가
버튼을 클릭하면 화면이 어떻게 변하는가
입력값이 잘못되었을 때 어떤 메시지가 나타나는가
요청 중 Loading 상태가 표시되는가
성공 / 빈 데이터 / 오류 상태가 올바르게 표현되는가

私にとってフロントエンドの検証は、Storybookのほうがより身近でした。Storybookはコンポーネントをアプリケーション全体から分離し、さまざまな状態を視覚的に確認・ドキュメント化することに強みがあります。一方、単体テストやコンポーネントテストでは、特定の操作の後に期待した結果が継続して維持されるかを自動的に検証します。

Storybook
→ 컴포넌트의 여러 상태를 시각적으로 확인하고 문서화

Unit / Component Test
→ 정해진 동작이 계속 유지되는지 자동으로 검증

フロントエンドの単体テストを調べる中で、Vitest、Testing Library、user-event、jsdomなどのツールがそれぞれ異なる役割を担っていることも整理できました。

Vitest
└─ 테스트 파일 실행, expect, mock / spy, 성공·실패 판정
   └─ Testing Library
      ├─ 컴포넌트 렌더링과 DOM 탐색
      └─ user-event : 클릭, 입력 등 사용자 행동 재현

jsdom
└─ Node.js 테스트 환경에 가상의 DOM 제공

Vitestはテストを実行し、結果を判定します。Testing LibraryはReactやVueのコンポーネントをレンダリングし、ユーザーの視点で要素を見つけられるよう支援します。user-eventはクリックや入力などの操作を再現し、jsdomは実際のブラウザを起動しなくてもDOMベースのテストを実行できる環境を提供します。

特にTesting Libraryは、CSS classや内部コンポーネント構造ではなく、ユーザーが認識できる役割や名前を基準に要素を探す方法を推奨しています。

screen.getByRole('button', { name: '로그인' });

つまり、「login-buttonというclassが存在するか」ではなく、「ユーザーがログイン用だと認識できるボタンが存在するか」を確認します。フロントエンドでも内部実装ではなく、ユーザーから観察できる動作を中心にテストできるということです。

4. フロントエンドでもRed → Green → Refactorは同じです

例えば、最初は0を表示し、増加ボタンを押すと値が1増えるCounterコンポーネントを作るとします。TDDであれば、コンポーネントを先に完成させるのではなく、次のようなテストから作成できます。

it('증가 버튼을 누르면 숫자가 1 증가한다', async () => {
    const user = userEvent.setup();
    render(<Counter />);

    expect(screen.getByText('현재 값: 0')).toBeVisible();
    await user.click(screen.getByRole('button', { name: '증가' }));
    expect(screen.getByText('현재 값: 1')).toBeVisible();
});

まだ増加機能がなければREDです。Countの状態とクリック動作を追加してテストが通過すればGREENになり、その後、構造を整理してREFACTORします。バックエンドとフロントエンドでは使用するツールやテスト対象が異なりますが、TDDの基本サイクルは同じです。

5. なぜAIコーディングアシスタントとTDDは相性がよいのでしょうか

ここまで見てきたように、TDDではテストが実装すべき動作を先に定義します。この構造にAIコーディングアシスタントが加わると、テストは仕様を超えて、作業中に利用できる基準になります。特に次の3つの点で、TDDの流れとよく合います。

完了条件を明確にできます

「ログイン機能を実装して」という依頼だけでは、どこまで実装すれば作業が完了したとみなすのか曖昧な場合があります。コードがコンパイルされること、APIが呼び出されること、画面がレンダリングされることは、それぞれ異なる完了条件です。

一方、あらかじめ定義されたテストがあれば、失敗はまだその動作を満たしていないという合図であり、成功は少なくともテストに表現された条件を満たしたという合図になります。AIコーディングアシスタントが実装結果を判断できる、比較的明確な基準が生まれるわけです。

失敗結果が次の行動のためのフィードバックになります

テストの失敗は、単に「間違っている」という結果だけを示すものではありません。期待値と実際の値の違いや、例外が発生した位置などの情報は、何を修正すべきかを判断する手がかりになります。

코드 작성
   ↓
테스트 실행
   ↓
결과 관찰
   ↓
코드 수정
   ↓
다시 테스트

AIコーディングアシスタントは、この結果に基づいてコードを修正し、再びテストできます。テストが、実装結果を観察できるフィードバックループを作ってくれるのです。

一度に実装する範囲を小さく保てます

AIコーディングアシスタントは短時間で大量のコードを生成できるため、要求範囲を超えて構造を拡張したり、まだ必要のない抽象化を追加したりすることもあります。TDDのGREEN段階では、現在失敗しているテストを通過させる最小限の実装を優先します。

小さなテストを一つずつ追加しながら実装すれば、「何を作るか」だけでなく「今どこまで作るか」も制限できます。コード生成の速度が上がった環境では、このような範囲の制御がむしろ重要になる可能性があります。

実装が終わった後も、再び安全網として残ります

TDDを適用したからといって、これまで使っていた検証用テストの意味がなくなるわけではありません。実装を導いたテストは、その後コードが修正されたりリファクタリングされたりする際に、既存の動作が壊れていないかを確認する回帰テストになります。

つまり、私がAIコーディングで使ってきた「実装後の検証」とTDDは、互いに置き換わる方法というより、テストが開発プロセスのより前段まで拡張される関係に近いものです。実装前には動作を定義し、実装中には成功と失敗を通じて方向性を示し、実装後には既存の動作を守る安全網として残ります。

6. おわりに

ここで、改めてタイトルの問いに戻りましょう。AI時代におけるテストの役割は、まったく新しいものに変わったというより、開発プロセスの前段まで拡張されたと考えるほうが適切に思えます。従来のように実装結果を検証すると同時に、TDDでは実装すべき動作と完了条件を先に定義し、実行結果を次の修正へのフィードバックとして利用できます。

AIコーディングによってProduction CodeとTest Codeを作成するコストがともに下がるほど、重要なのはどれだけ多くのコードを速く作れるかではなく、作られたコードが自分たちの意図した動作を満たしているかをどのように定義し、継続的に確認するかということなのかもしれません。その観点から、テストはAIコーディング時代においても重要な開発ツールであり続けると考えています。

お読みいただきありがとうございました。

Hhkk

Site footer