こんにちは!日々のテストコード書きに頭を悩ませていませんか?「機能の実装は終わったのに、テストの記述で半日が終わってしまった……」そんな経験、エンジニアなら誰しも一度はあるはずです。
今回は、世界中の開発者から絶賛されているAI特化型エディタ「Cursor」を使って、Python(Pytest)とTypeScript(Jest)の単体テストを爆速かつ高品質に自動生成するワークフローを徹底解説します。
これをマスターすれば、テスト駆動開発(TDD)のスピード感はそのままに、面倒なボイラープレート(定型コード)の記述から完全に解放されますよ。さあ、一緒に新しいコーディングの扉を開きましょう!
—
1. Cursorとは何か? なぜテスト自動生成で無双できるのか
Cursorは、エディタのデファクトスタンダードであるVS Codeをベースにフルスクラッチで再設計された、AIファーストのコードエディタです。単に「チャットでコードを教えてくれる」だけのツールではありません。
Cursorの真骨頂は、プロジェクト全体の文脈(Context)をAIが完全に理解している点にあります。
例えば、あなたが書いたTypeScriptの関数やPythonのクラスを選択し、「このコードの単体テスト書いて」と指示するだけで、AIは次のような離れ業をやってのけます。
- プロジェクト内で使われているテストフレームワーク(JestやPytest)を自動検出する
- 外部APIやデータベースなどの依存関係を自動でモック化(Mock)する
- 正常系だけでなく、「nullが渡されたら?」「例外が発生したら?」といったエッジケース(境界値)を網羅したテストケースを自ら考えてコード化する
ネットの海からコピペして微調整する作業は、今日で終わりにしましょう。
—
2. 基礎セットアップ:CursorでAIを最強の状態にする
まずは、Cursorのポテンシャルを120%引き出すための初期設定を行います。特別なプラグインを入れる必要はありません。Cursorには最初からAI機能が組み込まれています。
ステップ1:プロジェクトのインデックス作成(Codebase Indexing)
Cursorはプロジェクト内の全ファイルを読み込み、AIの頭脳に記憶させることができます。
1. 画面右上の歯車アイコン(Settings)をクリックし、`Cursor Settings`を開きます。
2. 左メニューの `Features` から `Codebase Indexing` を選択します。
3. リポジトリが正しく指定されていることを確認し、インデックス作成を有効にします。
> アーキテクトの知見: これにより、AIは「どのファイルでどんな型が定義されているか」を完全に把握します。テスト自動生成時に「あのモジュールどこだっけ?」とAIが迷わなくなるため、生成されるテストの精度が劇的に跳ね上がります。
ステップ2:カスタムインストラクション(Rules for AI)の設定
プロジェクトルートに `.cursorrules` というファイルを作成します。ここに「テストを書くときのお作法」を記述しておくことで、チーム全体のテスト品質を統一できます。
プロジェクトルートの `.cursorrules` の例:
AIへの行動指針(Cursor Rules)
テスト共通ルール
- テストフレームワークはPythonの場合は `pytest`、TypeScriptの場合は `Jest` を使用すること。
- テストカバレッジは極力100%を目指し、異常系や境界値のエッジケースを必ず含めること。
- 外部I/O(DB、API、ファイルシステム)は必ずモック化し、実環境に依存しないテストにすること。
このファイルを置いておくだけで、AIはあなたに代わって「プロジェクトのコーディング規約に沿った美しいテスト」を自発的に出力するようになります。
—
3. 実践!Python (Pytest) の単体テストを一撃で生成する
それでは、具体的なコードを使ってテスト生成のワークフローを体験していきましょう。まずはPythonのケースです。
対象のPythonコード (`calculator.py`)
消費税と割引を計算する、少しだけ複雑なビジネスロジックを持つクラスを想定します。
src/calculator.py
from typing import Optional
class PriceCalculator:
def __init__(self, tax_rate: float = 0.1):
# 税率の初期化(デフォルトは10%)
self.tax_rate = tax_rate
def calculate_total(self, price: int, discount_rate: Optional[float] = None) -> int:
“””
価格と割引率から税込金額を計算する
“””
if price < 0:
raise ValueError("価格は0円以上である必要があります。")
# 割引の適用
discounted_price = price
if discount_rate is not None:
if not (0.0 <= discount_rate <= 1.0):
raise ValueError("割引率は0.0から1.0の間である必要があります。")
discounted_price = price (1.0 - discount_rate)
# 税込計算(小数点以下は切り捨て)
total = discounted_price (1.0 + self.tax_rate)
return int(total)
Cursorでの生成手順(Composer機能を使用)
Cursorの神機能である Composer(Ctrl+I または Cmd+I) を使います。複数のファイルをまたいだ編集や新規作成が最も得意な機能です。
1. `Ctrl + I` (Windows) または `Cmd + I` (Mac) を押してComposerを開きます。
2. 対象のコードを選択した状態で、次のようにプロンプト(指示文)を入力します。
> プロンプト例:
> `src/calculator.py` に対する `pytest` 用の単体テストファイルを `tests/test_calculator.py` として新規作成してください。正常系、異常系(価格が負の場合、割引率が不正な場合)、および境界値テストを網羅してください。
生成されるテストコード (`tests/test_calculator.py`)
AIが数秒で書き上げたコードがこちらです。
tests/test_calculator.py
import pytest
from src.calculator import PriceCalculator
@pytest.fixture
def calculator():
“””標準的な税率(10%)を持つ計算機のフィクスチャ”””
return PriceCalculator(tax_rate=0.1)
class TestPriceCalculator:
def test_calculate_total_normal(self, calculator):
“””正常系:割引なしの計算”””
# 1000円の10%税込 = 1100円
assert calculator.calculate_total(1000) == 1100
def test_calculate_total_with_discount(self, calculator):
“””正常系:割引ありの計算”””
# 1000円から20%OFF = 800円、その10%税込 = 880円
assert calculator.calculate_total(1000, discount_rate=0.2) == 880
def test_calculate_total_invalid_price(self, calculator):
“””異常系:負の価格が渡された場合にValueErrorが発生すること”””
with pytest.raises(ValueError, match=”価格は0円以上である必要があります。”):
calculator.calculate_total(-100)
@pytest.mark.parametrize(“invalid_rate”, [-0.1, 1.1])
def test_calculate_total_invalid_discount_rate(self, calculator, invalid_rate):
“””異常系:範囲外の割引率が渡された場合にValueErrorが発生すること”””
with pytest.raises(ValueError, match=”割引率は0.0から1.0の間である必要があります。”):
calculator.calculate_total(1000, discount_rate=invalid_rate)
見事です! `pytest.fixture` やパラメータ化テスト (`@pytest.mark.parametrize`) まで完璧に使いこなし、人間が書くよりも洗練されたテストコードが一瞬で出来上がりました。
—
4. 実践!TypeScript (Jest) とモックの自動生成
次はTypeScriptです。実務では、外部APIを呼び出すサービス層など、「モック(Mock)」が必須となる複雑なテストに直面します。ここでもCursorは圧倒的な強さを発揮します。
対象のTypeScriptコード (`user-service.ts`)
外部APIクライアントに依存しているユーザー取得サービスのコードです。
// src/user-service.ts
export interface User {
id: string;
name: string;
email: string;
}
export interface ApiClient {
fetchUserById(id: string): Promise
}
export class UserService {
constructor(private apiClient: ApiClient) {}
async getFormattedUserName(id: string): Promise
if (!id || id.trim() === “”) {
throw new Error(“IDが無効です。”);
}
try {
const user = await this.apiClient.fetchUserById(id);
return `${user.name} (${user.email})`;
} catch (error) {
// 外部APIのエラーをラップしてスローする
throw new Error(`ユーザーの取得に失敗しました: ${id}`);
}
}
}
Cursorでの生成手順とプロンプト
インラインチャット(`Ctrl + L` または `Cmd + L`)またはComposerを使い、次のように指示します。
> プロンプト例:
> `src/user-service.ts` のJestによる単体テストを `src/__tests__/user-service.test.ts` に作成してください。`ApiClient` はJestのモック(`jest.Mocked
生成されるテストコード (`src/__tests__/user-service.test.ts`)
AIが生成したJestのコードがこちらです。
// src/__tests__/user-service.test.ts
import { UserService, ApiClient, User } from ‘../user-service’;
describe(‘UserService’, () => {
// ApiClientのモック型を作成
let mockApiClient: jest.Mocked
let userService: UserService;
beforeEach(() => {
// 各テストの実行前にモックを初期化
mockApiClient = {
fetchUserById: jest.fn(),
};
userService = new UserService(mockApiClient);
});
describe(‘getFormattedUserName’, () => {
it(‘正常系:有効なIDが渡された場合、フォーマットされたユーザー名を返すこと’, async () => {
// 準備(Mockの戻り値を定義)
const mockUser: User = { id: ‘1’, name: ‘山田 太郎’, email: ‘yamada@example.com’ };
mockApiClient.fetchUserById.mockResolvedValueOnce(mockUser);
// 実行
const result = await userService.getFormattedUserName(‘1’);
// 検証
expect(result).toBe(‘山田 太郎 (yamada@example.com)’);
expect(mockApiClient.fetchUserById).toHaveBeenCalledWith(‘1’);
expect(mockApiClient.fetchUserById).toHaveBeenCalledTimes(1);
});
it(‘異常系:空のIDが渡された場合、バリデーションエラーをスローすること’, async () => {
// 実行 & 検証
await expect(userService.getFormattedUserName(”)).rejects.toThrow(‘IDが無効です。’);
// APIが呼び出されていないことを確認
expect(mockApiClient.fetchUserById).not.toHaveBeenCalled();
});
it(‘異常系:API呼び出しが失敗した場合、エラーをラップしてスローすること’, async () => {
// 準備(APIがエラーを吐くようにモック)
mockApiClient.fetchUserById.mockRejectedValueOnce(new Error(‘Network Error’));
// 実行 & 検証
await expect(userService.getFormattedUserName(‘999’)).rejects.toThrow(
‘ユーザーの取得に失敗しました: 999’
);
});
});
});
どうでしょうか? 面倒なJestのモック定義(`jest.fn()` や `mockResolvedValueOnce` など)が、プロンプトを投げるだけで完璧に記述されています。開発者は「テストの仕様が正しいか」をレビューするだけでよくなります。
—
5. 現場で使える!テスト生成をさらに加速させるプロンプトの極意
最後に、AIからより精度の高いテストを引き出すためのプロンプトのコツをいくつか伝授します。
1. カバレッジやケースを指定する
- 「正常系2つ、異常系3つ、境界値1つを作成して」と具体的に数を指定すると、AIの思考がシャープになり、網羅性が上がります。
2. 既存のテストスタイルを学習させる
- リポジトリ内にすでに書かれているきれいなテストファイルを一つ指定し、「このファイルの書き方・アテストのスタイルを模倣してテストを書いて」と指示すると、チームのスタイルに完全に馴染んだコードが生成されます。
3. 「自己修復(Self-Correction)」ループを使う
- もし生成されたテストを実行してエラーが出たら、そのエラーログ全体をコピーしてそのままCursorのチャットに貼り付けてください。 「このエラーを修正するようにテストコードを直して」と頼むだけで、AIが原因を分析し、一瞬で修正版を提示してくれます。
—
まとめ:毎日のコーディングを劇的に楽にしよう
今回は、Cursorを活用したPython(Pytest)およびTypeScript(Jest)のテスト自動生成ワークフローを解説しました。
- CursorのCodebaseインデックスとComposer機能を組み合わせることで、プロジェクトの文脈を理解した高品質なテストが爆速で手に入る。
- `.cursorrules` を活用することで、チーム全体で一貫した品質のテストコードを維持できる。
- モック化やエッジケースの洗い出しといった面倒な作業はAIに任せ、人間は「設計と仕様の確認」に集中する。
これをマスターすれば、テストを書く苦痛から解放され、機能開発のスピードとコードの信頼性が同時に爆発的に向上します。ぜひ、あなたのプロジェクトでも今日から試してみてくださいね。毎日のコーディングが劇的に楽しく、楽になりますよ!