その他

【Devin】AIを使うなら ”テスト駆動開発(TDD)” で開発しよう!

その他
この記事は約10分で読めます。

はじめに

みなさんこんにちは。インプルの岩崎です。

みなさん、Devin使ってますか?
弊社でも先日勉強会を開き、いよいよDevinの本格導入へ向けて動き出しています。Devinについての詳しい記事は、こちらをご覧ください!

多かったエンジニアの声:「テストについて」

勉強会をやってみると、現場で働いているエンジニアさんからは、Devinがコードを書いてくれる凄さを実感してくれたものの、テストの過程についての質問が多くありました。

開発におけるテスト

ソフトウェア開発における「テスト」には、明確で体系的な意味があります。
開発におけるテストとは、一言でいうと「プログラムが予定通りに動くかどうかを確認する工程やコード」のことです。

プログラムというのは、一見ちゃんと動いていても、特定の条件によってはエラーが出たり、入力によってはバグが出たりと、問題が起こりやすいです。
なので、「こういう条件ならこう動くはず」という正しさを事前に決めておいて、それを機械的に確認するのが「テスト」です。

テストの種類には、ざっくり3つあり、chatGPTがまとめてくれました。

種類内容
単体テスト(Unit Test)関数やAPIなど、1つの部品の動作確認add(2, 3)5 を返すか?
結合テスト(Integration Test)部品同士が連携したときの動作確認API → DB → 結果が返る、が成立するか?
E2Eテスト(End to End)ユーザー操作に近い、全体の流れを確認「ユーザー登録→ログイン→プロフィール変更」が成功するか?

テスト駆動開発(TDD)

開発手法には、テストを中心に進めるものもあります。それが、テスト駆動開発(TDD)です。

「テストコードを書いてから実装を書く」開発スタイルで、TDDTest Driven Development の略です。通常の開発とは、次の表のような違いがあります。(これもchatGPTまとめです!)

通常の開発TDD(テスト駆動開発)
1. コードを書く1. テストを書く(失敗する状態)
2. 動かして確認2. 必要最小限の実装を書く
3. 必要ならテストを書く    3. テストが通るか確認(Green)
4. 実装をきれいに整理(Refactor)    

この「Red(失敗)→Green(成功)→Refactor(整理)」というサイクルを小さな単位でぐるぐる回すことが特徴の開発手法となっています。ちなみに、ここでのテストは、さきほどの3つの表の中でいう、「単体テスト」と大きな関わりがあります。

テスト駆動開発 ≒ 単体テストを駆動力にした開発手法

テスト駆動開発は、「テストを書いてからコードを書く」進め方ですが、ここで書かれるテストのほとんどは、「単体テスト」です。

単体テストとは、プログラムの最小単位のテストですので、これを元に最小限の実装を書き、テストが通ることを確認して、コードを整理する(リファクタリング)流れです。

Devin × テスト駆動開発(TDD)は相性がいい?

Devinでの開発記事を見たことがある人には、もしかしたらテスト駆動開発との相性について、「良い」と言われている記事を見たことがあるかもしれません。
それは正解で、テスト駆動開発の手法は、DevinのようなAIにとって非常に相性がいいのです。

テストがあれば、目的(プログラムの動作の正解)がはっきりしますし、単体テストを書くタスクを先に指示することで、テストを満たすコードを自動で作ってくれます。だからこそ、「DevinにはTDDでタスクを出すと良い」という話がよく出てくるのです。chatGPTの表も参考にどうぞ!

理由内容
🎯 テストが目的そのものになるDevinはテストを「達成すべきゴール」として扱える
🧭 指示がぶれない曖昧な自然言語よりも、テストで要件を伝える方が明確
🛠 実装が高速&安定テストに沿って最小限のコードを書けるため効率的
🔄 自動修正・改善も可能テストを回して壊れていないことを確認しながら進められる

ちなみに、Devin(AI) × そのほかのテストは?

単体テストは先ほどのテスト駆動開発でも有効と答えたように、DevinのようなAIとベストマッチですが、結合テスト・E2Eテストはどうなのでしょうか?
これについては、できるが注意が必要という回答になります。chatGPTがまとめてくれた表を見てみましょう。

テストDevin対応度コメント
単体テスト指示なしでもよく書くレベル。TDD指定でさらに安定
結合テスト環境と仕様を明確に書けば精度が高い
E2Eテスト実装コードは書けるが、実行や検証は人が必要

このように、結合テストまでは、細かい指示を出せば精度が高く対応してくれるようですが、E2Eテストは、実行・確認・エラー検証などは開発者側の補助が必要です。

Devinは実ブラウザでは動かせないため、E2E用のコードを生成することはできるが、実行環境までは人間が用意する必要があります。

Devinでテスト駆動開発をやらせてみよう

今回は、Devinに偶数判定関数を作ってもらうことにしましょう。その名の通り、与えられた整数が偶数かどうか、判定する関数になります。

せっかくなので、前回も記事にしたissueの与え方 vs テスト駆動開発で、2パターンでやらせてみましょう。Devinに与えるタスクの要件をまとめた記事はこちらになります。まだご覧になってない方は、ぜひチェック!

通常のissueパターン

まずは通常のパターンでissueをつくります。

Pythonで偶数かどうかを判定する関数 is_even(n) を実装し、pytestを用いて簡単なテストコードを書くようDevinに指示しています。実装要件として「偶数であればTrue、奇数であればFalseを返す」ことを明示しており、完了基準では「正しく動作する関数」と「テストコードの記述」が求められています。TDDの指定はなく、あくまで通常の開発スタイルとして、機能とテストが揃っていればOKという指示です。

# 技術要件
- 言語: Python
- テスト: pytestを使用

# 前提
整数を受け取って偶数であるかを返す関数 `is_even(n)` を作成してください。

# 実装要件
- 入力 `n` は整数
- 偶数であれば `True`、奇数なら `False` を返す

# 完了基準
- 正しく動作する関数が作成されていること
- pytestで簡単なテストコードが書かれていること

実際にやらせてみたところ、nを2で割った余りの結果でTrue(偶数)、False(奇数)を返しているので、処理については問題なさそうです。
関数の説明も入れてくれていますが、「ゼロ」や「負の数」の扱いについては明示されておらず、テスト観点ではやや曖昧さが残ります。

def is_even(n):
    """
    Check if a given integer is even.
    
    Args:
        n (int): The integer to check
        
    Returns:
        bool: True if n is even, False if n is odd
    """
    return n % 2 == 0

作ってくれたコードはGitHubにありますので、ぜひご覧ください。

GitHub - iwasakiterukazuimpl/devin_is-even at normal
Contribute to iwasakiterukazuimpl/devin_is-even development by creating an account on GitHub.

テスト駆動開発(TDD)パターン

続いて、テスト駆動開発パターンのissueです。

偶数判定関数 is_even(n) をテスト駆動開発(TDD)のスタイルで作成するようにDevinへ指示しています。最初にテストコードだけを作成し、それが失敗する状態から始め、最小限の実装でテストを通し、最後にコードを整えるというTDDの3ステップ(テスト→実装→リファクタリング)を明確に指定しています。また、テスト内容も例を含めて具体的に記載しており、Devinが意図通りに実行できるように設計されています。

# 技術要件
- 言語: Python
- テスト: pytestを使用

# 前提
偶数判定をする関数 `is_even(n)` をテスト駆動開発(TDD)で作成してください。
まずはテストコードを書き、失敗させ、その後テストが通るように関数を実装してください。

# 実装要件
以下の要件を満たすテストをまず作成してください(pytest形式):
- 関数名:`is_even`
- 要件:偶数であれば `True`、奇数であれば `False` を返すこと
- 例:
  - `is_even(2)` → True
  - `is_even(3)` → False

- 最初はこのテストが失敗する状態でスタートしてください
- 最小限の実装でテストが通るようにしてください
- 最後にリファクタリングを行ってください

# 完了基準
- pytestでテストが通ること
- テスト → 実装 → リファクタの3ステップを踏んでいること
- 実装とテストの両方がPR内に含まれていること

こちらも同様に、処理としては同じものを作ってきました。(当たり前)
関数の説明も同様に入れていますが、先ほどとの違いは、ゼロや負の整数に対する挙動も明記されており、どのような数字が来ても抜かりなく対応できるように作られています。
Examples: >>> is_even(2) の部分は、doctest形式の例です。Pythonではこの形式のテストも可能で、コメントがそのままテストとして使えるメリットがあります。

def is_even(n):
    """
    Check if a given integer is even.
    
    An integer is even if it is divisible by 2 with no remainder.
    Zero is considered even.
    
    Args:
        n (int): The integer to check
        
    Returns:
        bool: True if n is even, False if n is odd
        
    Examples:
        >>> is_even(2)
        True
        >>> is_even(3)
        False
        >>> is_even(0)
        True
        >>> is_even(-2)
        True
    """
    return n % 2 == 0

作ってくれたコードは、こちらも同様にGitHubにありますので、ぜひご覧ください。

GitHub - iwasakiterukazuimpl/devin_is-even at tdd
Contribute to iwasakiterukazuimpl/devin_is-even development by creating an account on GitHub.

開発まとめ

さて、それぞれの開発方法でissueを読み込ませてきましたが、いかがだったでしょうか?

処理する部分は当然同じような結果となりましたが、テスト駆動開発のissueでは、ゼロや負の数といったケースにも対応できており、アプローチ方法にも違いが現れてきた結果となりました。

テスト駆動開発では、コードを書く前に“どう壊れるか”を考えることができるので、これが関数の精度だけでなく、仕様の明確さやテストの網羅性にもつながったのではないかと考えられます。

通常 vs TDD どっちがいいの!?

さて、今回は2通りのパターンで偶数判定関数を作っていきました。
その中で、通常のissueを与えるやり方と、テスト駆動開発の大きな違いは、Devinに何をゴールとさせているかになると思います。

通常のissueでは、Devinにただ作るものを指示しているだけですが、テスト駆動開発では、テストを作らせてから、そのテストに向かって開発を進めさせる、という指示をしています。つまり、正解に向かって実装しているわけです。

Devin本人にも、作業終わりの時間に聞いてみたところ、「TDDの方が一般的に精度が高い」といっていました笑。でも、Devinが言っているように、テスト駆動開発の方が、少し時間がかかった気がします。

それぞれのケースに応じたAI開発を!

ここまでご覧いただき、ありがとうございました。

ここまで、AIによる開発はテスト駆動開発をお勧めしてきましたが、結局はケースに応じて開発スタイルも変わってくると思います。先ほども紹介されたように、一度テストを書いてから作業に入るので、時間はかかってしまうというデメリットは持ち合わせています。なので、スピードが求められる開発においては、通常のissueで進めた方が早そうです。

もちろん、Devinは通常のissueから作成されたものに対しても、テストをさせることができます。実は、先ほどの通常issue版でも、テストをするよう記述がされていました。なので、そのような開発手法も当然ありです!

スピードを重視するか、精度を重視するか、ケースに応じた開発をしてAIをフル活用していきましょう!