Apex テストクラスを生成・検証する際に使用します。TestDataFactory パターン(テスト用データを効率的に作成する仕組み)、大量データ対応(251件以上のレコード)、モック(動作シミュレーション)、アサーション(動作確認)、そして計画的なテスト修正サイクルに対応しています。 次のような場合に使用: - Apex テストクラスを新規作成する(トリガー、サービス、コントローラー、バッチジョブ、キューイング処理、外部 API 連携用) - テストカバレッジ(動作確認の範囲)を向上させたい - 失敗している Apex テストをデバッグ・修正する - テスト実行やカバレッジ分析を行う *Test.cls ファイルの編集、sf apex run test コマンドの実行、カバレッジレポート表示時に自動で起動します。 **注意**: 本番環境用の Apex コード生成には使用しないでください(platform-apex-generate を代わりに使用)。Jest や LWC テストには対応していません。
Use to generate and validate Apex test classes with TestDataFactory patterns, bulk testing (251+ records), mocking, assertions, and disciplined test-fix loops. Use when creating Apex test classes (for triggers, services, controllers, batch jobs, queueables, and callouts), improving coverage, debugging or fixing failing Apex tests, or running test/coverage analysis. Triggers on *Test.cls files, sf apex run test, coverage reports. Do NOT trigger for production Apex (use platform-apex-generate) or Jest/LWC tests.
本番環境対応のApexテストクラスを生成し、カバレッジ(コード網羅率)分析を伴った体系的なテスト修正サイクルを実行します。
1メソッドにつき1つの動作 — 各テストメソッドは1つのシナリオのみを検証します。正常系、異常系、一括処理のテストは分けてください。関連しているが異なる入力値(例:null と空文字列)を1つのメソッドに混在させずに、_NullInput_ と _EmptyInput_ として別々のテストメソッドを作成してください
一括処理テストも含める — 251件以上のレコードでテストし、200件のトリガー一括処理境界を越えてください。Batch Apex の例外: テストコンテキストでは execute() が1回だけ実行されるため、batchSize >= testRecordCount に設定してください。references/async-testing.md を参照
テストデータの分離 — すべての @TestSetup はレコード作成を TestDataFactory クラスに委譲してください。存在しない場合は先に作成してください。@TestSetup 内でレコードリストを直接構築しないでください。また、組織データ(SeeAllData=false)やハードコード化されたIDに依存しないでください。重複ルール処理については references/test-data-factory.md を参照
意味のあるアサーション — テストデータ設定から計算した正確な期待値を使用してください。値が確定的である場合、範囲指定アサーションや概数カウントは使用しないでください。常に失敗メッセージを含めてください。references/assertion-patterns.md を参照
Assert クラスのみ使用 — Assert.areEqual、Assert.isTrue、Assert.fail などを使用してください。レガシーの System.assert、System.assertEquals、System.assertNotEquals は使用しないでください
外部連携をモック化 — 外部連携にはHttpCalloutMock、SOSL にはTest.setFixedSearchResults、データベース分離にはDMLモッククラスを使用してください。コンストラクタインジェクション経由のテスト設計を心がけてください。references/mocking-patterns.md を参照
負の経路もテスト — 正常系だけでなく、エラー処理と例外シナリオも検証してください
startTest/stopTest で囲む — Test.startTest() と Test.stopTest() をペアで使用し、ガバナー制限(処理上限)をリセットし、非同期処理を強制実行してください
テスト対象のコードを常に Test.startTest() / Test.stopTest() で囲んでください:
| アンチパターン | 修正方法 |
|---|---|
| ループ内のSOQL/DML | ループ前に1回クエリを実行し、ルックアップに Map<Id, SObject> を使用 |
| アサーション内のマジックナンバー | 期待値をセットアップ定数から導出 |
| 巨大なテストクラス(500行超) | 動作領域ごとに複数のテストクラスに分割 |
| 長いテストメソッド(30行超) | Given/When/Then をヘルパーメソッドに抽出 |
汎用的な Exception キャッチ |
予想される具体的な型をキャッチ(例:DmlException) |
テストを生成または修正する前に、以下を特定してください:
アセットテンプレートとリファレンスドキュメントの構造、命名規則、パターンを適用してください。
必須 — ファイル成果物: すべてのテストクラスについて、以下の2つのファイルを作成してください:
{ClassName}Test.cls — テストクラス(assets/test-class-template.cls を出発点として使用){ClassName}Test.cls-meta.xml — メタデータファイル:<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<status>Active</status>
</ApexClass>
プロジェクトに TestDataFactory が存在しない場合、assets/test-data-factory-template.cls を使用して TestDataFactory.cls + TestDataFactory.cls-meta.xml を作成してください。
@TestSetup
static void setupTestData() {
List<Account> accounts = TestDataFactory.createAccounts(251, true);
}
Given/When/Then を使用してください:
@isTest
static void shouldUpdateStatus_WhenValidInput() {
// Given
List<Account> accounts = [SELECT Id FROM Account];
// When
Test.startTest();
MyService.processAccounts(accounts);
Test.stopTest();
// Then
List<Account> updated = [SELECT Id, Status__c FROM Account];
Assert.areEqual(251, updated.size(), 'All accounts should be processed');
}
try/catch と Assert.fail を使用して予想される例外を検証してください:
@isTest
static void shouldThrowException_WhenInvalidInput() {
// Given
List<Account> emptyList = new List<Account>();
// When/Then
Test.startTest();
try {
MyService.processAccounts(emptyList);
Assert.fail('Expected MyCustomException to be thrown');
} catch (MyCustomException e) {
Assert.isTrue(e.getMessage().contains('cannot be empty'),
'Exception message should indicate empty input');
}
Test.stopTest();
}
should[予想される結果]_When[条件]: shouldSendNotification_WhenOpportunityClosedWon[対象または操作]_[条件]_[予想される結果]: AccountUpdate_ChangeName_Successデバッグ時は狭い範囲から始め、修正が安定した後に拡大してください。
# 1つのテストクラス
sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>
# 特定のテストメソッド
sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>
# すべてのローカルテスト
sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>
以下に注目してください:
テストが失敗した場合、体系的な修正ループを実行してください(最大3回のイテレーション — 依然として失敗している場合は根本原因を明らかにしてください):
platform-apex-generate スキルに委譲してください| レベル | カバレッジ | 目的 |
|---|---|---|
| 本番環境デプロイ | 最小75% | Salesforce が必須 |
| 推奨 | 90%以上 | ベストプラクティス目標 |
| 重要な経路 | 100% | ビジネス上重要なコード |
すべての経路をカバー:正常系、異常系/例外、一括処理(251件以上のレコード)、外部連携/非同期処理
| コンポーネント | 主要なテストシナリオ |
|---|---|
| トリガー | 一括挿入/更新/削除、再帰ガード(無限ループ防止)、フィールド変更検出 |
| サービス | 有効/無効な入力値、一括操作、例外処理 |
| コントローラー | ページロード、アクションメソッド、ビュー状態 |
| バッチ | start/execute/finish、スコープ一致(バッチサイズ >= レコード数)、Database.Stateful の追跡、エラー処理、チェーン(個別メソッド — finish() が Database.executeBatch() を呼び出すと UnexpectedException がスロー) |
| キューに登録可能な処理 | チェーン(テストでは最初のジョブのみ実行)、一括処理、エラー処理、Test.startTest() 前に外部連携モックを設定 |
| 外部連携 | 成功レスポンス、エラーレスポンス、タイムアウト |
| セレクター(クエリヘルパー) | 有効/null/空の入力値、一括処理(251件以上)、フィールド補完、ソート順、System.runAs 経由の WITH USER_MODE |
| スケジュール済み | execute(null) 経由の直接実行、CronTrigger クエリ経由のCRON登録 |
| プラットフォームイベント | Test.enableChangeDataCapture()、Test.getEventBus().deliver()、購読者側の影響を検証 |
テストクラスごとの成果物:
{ClassName}Test.cls + {ClassName}Test.cls-meta.xml(テスト対象クラスのAPIバージョンに合わせる;デフォルト 66.0)TestDataFactory.cls + TestDataFactory.cls-meta.xml(既に存在しない場合)詳細なパターンについては必要に応じて参照してください:
| リファレンス | 使用時期 |
|---|---|
| references/test-data-factory.md | TestDataFactory パターン、フィールド上書き、重複ルール処理 |
| references/assertion-patterns.md | アサーション ベストプラクティス、アンチパターン、一般的な落とし穴 |
| references/mocking-patterns.md | HttpCalloutMock、DMLモック、StubProvider、SOSL、メール、プラットフォームイベント |
| references/async-testing.md | バッチ、キューに登録可能な処理、Future、スケジュール済みジョブのテスト |
Generate production-ready Apex test classes and run disciplined test-fix loops with coverage analysis.
_NullInput_ and _EmptyInput_ as separate test methodsexecute() invocation runs, so set batchSize >= testRecordCount. See references/async-testing.md@TestSetup must delegate record creation to a TestDataFactory class. If none exists, create one first. Never build record lists inline in @TestSetup. Never rely on org data (SeeAllData=false) or hardcoded IDs. For duplicate rule handling, see references/test-data-factory.mdAssert class only — Assert.areEqual, Assert.isTrue, Assert.fail, etc. Never use legacy System.assert, System.assertEquals, or System.assertNotEqualsHttpCalloutMock for callouts, Test.setFixedSearchResults for SOSL, DML mock classes for database isolation. Design for testability via constructor injection. See references/mocking-patterns.mdTest.startTest() with Test.stopTest() to reset governor limits and force async executionAlways wrap the code under test in Test.startTest() / Test.stopTest():
| Anti-Pattern | Fix |
|---|---|
| SOQL/DML inside loops | Query once before the loop; use Map<Id, SObject> for lookups |
| Magic numbers in assertions | Derive expected values from setup constants |
| God test class (>500 lines) | Split into multiple test classes by behavior area |
| Long test methods (>30 lines) | Extract Given/When/Then into helper methods |
Generic Exception catch |
Catch the specific expected type (e.g., DmlException) |
Before generating or fixing tests, identify:
Apply the structure, naming conventions, and patterns from the asset templates and reference docs.
MANDATORY — File Deliverables: For every test class, create BOTH files:
{ClassName}Test.cls — the test class (use assets/test-class-template.cls as starting point){ClassName}Test.cls-meta.xml — the metadata file:<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<status>Active</status>
</ApexClass>
If no TestDataFactory exists in the project, create TestDataFactory.cls + TestDataFactory.cls-meta.xml using assets/test-data-factory-template.cls.
@TestSetup
static void setupTestData() {
List<Account> accounts = TestDataFactory.createAccounts(251, true);
}
Use Given/When/Then:
@isTest
static void shouldUpdateStatus_WhenValidInput() {
// Given
List<Account> accounts = [SELECT Id FROM Account];
// When
Test.startTest();
MyService.processAccounts(accounts);
Test.stopTest();
// Then
List<Account> updated = [SELECT Id, Status__c FROM Account];
Assert.areEqual(251, updated.size(), 'All accounts should be processed');
}
Use try/catch with Assert.fail to verify expected exceptions:
@isTest
static void shouldThrowException_WhenInvalidInput() {
// Given
List<Account> emptyList = new List<Account>();
// When/Then
Test.startTest();
try {
MyService.processAccounts(emptyList);
Assert.fail('Expected MyCustomException to be thrown');
} catch (MyCustomException e) {
Assert.isTrue(e.getMessage().contains('cannot be empty'),
'Exception message should indicate empty input');
}
Test.stopTest();
}
should[ExpectedResult]_When[Scenario]: shouldSendNotification_WhenOpportunityClosedWon[SubjectOrAction]_[Scenario]_[ExpectedResult]: AccountUpdate_ChangeName_SuccessStart narrow when debugging; widen after the fix is stable.
# Single test class
sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>
# Specific test methods
sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>
# All local tests
sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>
Focus on:
When tests fail, run a disciplined fix loop (max 3 iterations — stop and surface root cause if still failing):
platform-apex-generate skill| Level | Coverage | Purpose |
|---|---|---|
| Production deploy | 75% minimum | Required by Salesforce |
| Recommended | 90%+ | Best practice target |
| Critical paths | 100% | Business-critical code |
Cover all paths: positive, negative/exception, bulk (251+ records), callout/async.
| Component | Key Test Scenarios |
|---|---|
| Trigger | Bulk insert/update/delete, recursion guard, field change detection |
| Service | Valid/invalid inputs, bulk operations, exception handling |
| Controller | Page load, action methods, view state |
| Batch | start/execute/finish, scope matching (batch size >= record count), Database.Stateful tracking, error handling, chaining (separate methods — finish() calling Database.executeBatch() throws UnexpectedException) |
| Queueable | Chaining (only first job runs in tests), bulkification, error handling, callout mocks before Test.startTest() |
| Callout | Success response, error response, timeout |
| Selector | Valid/null/empty inputs, bulk (251+), field population, sort order, WITH USER_MODE via System.runAs |
| Scheduled | Direct execution via execute(null), CRON registration via CronTrigger query |
| Platform Event | Test.enableChangeDataCapture(), Test.getEventBus().deliver(), verify subscriber side effects |
Deliverables per test class:
{ClassName}Test.cls + {ClassName}Test.cls-meta.xml (match API version of class under test; default 66.0)TestDataFactory.cls + TestDataFactory.cls-meta.xml (if not already present)Load on demand for detailed patterns:
| Reference | When to use |
|---|---|
| references/test-data-factory.md | TestDataFactory patterns, field overrides, duplicate rule handling |
| references/assertion-patterns.md | Assertion best practices, anti-patterns, common pitfalls |
| references/mocking-patterns.md | HttpCalloutMock, DML mocking, StubProvider, SOSL, Email, Platform Events |
| references/async-testing.md | Batch, Queueable, Future, Scheduled job testing |
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。