mcpField
S
sf-test

sf-test

github.com/clientell-aiテスト・品質20102 回インストール32 週間前に更新
リポジトリ

sf-test について

Salesforce 向けに、幅広いシナリオと明確なアサーションを含む Apex テストクラスの作成を支援するスキルです。テストデータの準備、非同期処理やプラットフォーム機能のテスト、実行とカバレッジ確認を案内します。

主な機能

  1. 01正常・異常・一括・権限・境界値テスト
  2. 02TestDataFactory によるテストデータ準備
  3. 03非同期処理・コールアウト・イベントのテストパターン
  4. 04Salesforce CLI によるテスト実行の案内

活用シーン

  1. 01Apex メソッドやコードパスのテスト作成
  2. 02200 件以上のレコードを使った一括処理の検証
  3. 03非同期処理、コールアウト、REST エンドポイントのテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-test

インストール後、Claude Code で/sf-testを入力して読み込まれたか確認します。

同梱ファイルreferences/test-patterns.md

詳細

Apex テストクラスジェネレーター

Salesforce のテストクラスのスペシャリストです。意味のあるアサーションを含み、85%以上のコードカバレッジを達成する包括的なテストクラスを生成します。

テストクラスの構成

必須パターン
@IsTest
private class MyClassTest {

    @TestSetup
    static void makeData() {
        // Use TestFactory for all record creation
        List<Account> accounts = TestDataFactory.createAccounts(200);
        insert accounts;

        List<Contact> contacts = TestDataFactory.createContacts(accounts);
        insert contacts;
    }

    @IsTest
    static void testMethodName_positiveScenario() {
        // Arrange
        List<Account> accounts = [SELECT Id, Name FROM Account WITH USER_MODE];

        // Act
        Test.startTest();
        MyClass.myMethod(accounts);
        Test.stopTest();

        // Assert
        List<Account> results = [SELECT Id, Status__c FROM Account WITH USER_MODE];
        System.assertEquals(200, results.size(), 'All accounts should be processed');
        for (Account acc : results) {
            System.assertNotEquals(null, acc.Status__c, 'Status should be set');
        }
    }
}
テストシナリオ(以下をすべて生成)
  1. 正常系テスト:有効なデータを使った正常なケース
  2. 異常系テスト:無効なデータ、null 入力、空のリスト
  3. 一括処理テスト:一括処理への対応を検証するため、200 件以上のレコードを使用
  4. 権限テスト:制限付きユーザープロファイルでテスト
  5. 境界値テスト:境界ケース(0 件、1 件、最大件数)
権限テストのパターン
@IsTest
static void testMethod_restrictedUser() {
    User restrictedUser = TestDataFactory.createStandardUser();
    insert restrictedUser;

    System.runAs(restrictedUser) {
        Test.startTest();
        try {
            MyClass.myMethod(testData);
            System.assert(false, 'Should have thrown exception');
        } catch (SecurityException e) {
            System.assert(e.getMessage().contains('access'),
                'Should throw security exception');
        }
        Test.stopTest();
    }
}
コールアウトのモックパターン
@IsTest
private class MyCalloutClassTest {

    private class MockHttpResponse implements HttpCalloutMock {
        private Integer statusCode;
        private String body;

        MockHttpResponse(Integer statusCode, String body) {
            this.statusCode = statusCode;
            this.body = body;
        }

        public HttpResponse respond(HttpRequest req) {
            HttpResponse res = new HttpResponse();
            res.setStatusCode(this.statusCode);
            res.setBody(this.body);
            return res;
        }
    }

    @IsTest
    static void testCallout_success() {
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, '{"status":"ok"}'));

        Test.startTest();
        String result = MyCalloutClass.makeCallout();
        Test.stopTest();

        System.assertEquals('ok', result, 'Should return success status');
    }

    @IsTest
    static void testCallout_failure() {
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse(500, '{"error":"fail"}'));

        Test.startTest();
        try {
            MyCalloutClass.makeCallout();
            System.assert(false, 'Should throw on 500');
        } catch (CalloutException e) {
            System.assert(true, 'Exception expected on server error');
        }
        Test.stopTest();
    }
}

ルール

  • レコード ID をハードコードしないでください。必ず @TestSetup でクエリするか、作成してください
  • ガバナ制限をリセットするため、必ず Test.startTest() と Test.stopTest() を使用してください
  • 説明的なメッセージを付けて、必ず System.assertEquals/System.assertNotEquals を使用してください
  • 一括処理のシナリオでは、必ず最低 200 件のレコードを使ってテストしてください
  • private メソッド/変数を public にする代わりに、@TestVisible を使用してください
  • TestDataFactory クラスが存在しない場合は作成してください
  • 特定のプラットフォーム機能をテストする場合を除き、SeeAllData=true は使用しないでください
  • 同期処理と非同期処理の両方のパス(future、queueable、batch)をテストしてください

TestDataFactory のパターン

@IsTest
public class TestDataFactory {

    public static List<Account> createAccounts(Integer count) {
        List<Account> accounts = new List<Account>();
        for (Integer i = 0; i < count; i++) {
            accounts.add(new Account(
                Name = 'Test Account ' + i
            ));
        }
        return accounts;
    }

    public static User createStandardUser() {
        Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1];
        return new User(
            FirstName = 'Test',
            LastName = 'User',
            Email = '[email protected]',
            Username = 'testuser' + DateTime.now().getTime() + '@example.com',
            Alias = 'tuser',
            TimeZoneSidKey = 'America/Los_Angeles',
            LocaleSidKey = 'en_US',
            EmailEncodingKey = 'UTF-8',
            ProfileId = p.Id,
            LanguageLocaleKey = 'en_US'
        );
    }
}
非同期テストのパターン
  • @future:Test.stopTest() の後に実行されます。stopTest の後に副作用をアサートしてください
  • Batch:Test.startTest()/Test.stopTest() の間に Database.executeBatch() を呼び出してください
  • Queueable:startTest/stopTest の間に System.enqueueJob() を呼び出してください。テストではチェーンの深さは 1 に制限されます
  • Schedulable:startTest/stopTest の間に System.schedule() を呼び出し、その後 CronTrigger をアサートしてください
プラットフォームイベントと CDC のテスト
  • プラットフォームイベント:公開後に Test.getEventBus().deliver() を呼び出し、同期配信を強制してください
  • Change Data Capture:テストのセットアップで Test.enableChangeDataCapture() を呼び出し、DML の後に Test.getEventBus().deliver() を呼び出してください
Stub API(依存性注入)

System.StubProvider インターフェースと Test.createStub() を使用すると、データベースにアクセスせずに依存関係をモックできます。

Test.loadData()

静的リソース内の CSV から一括テストデータを読み込みます:Test.loadData(Account.sObjectType, 'TestAccounts')

Mixed DML の回避策

System.runAs() を使用して、同じテスト内でセットアップオブジェクトの DML(User、Profile)と非セットアップオブジェクトの DML を分離してください。

特殊オブジェクトのテスト
  • Product2/PricebookEntry のテストには Test.getStandardPricebookId() を使用してください
  • @RestResource エンドポイントのテストには RestContext.request = new RestRequest() を使用してください

注意点

  • @TestSetup のデータはテストメソッド間で共有されます(分離されません)。各メソッドにはリセットされたコピーが渡されます
  • SeeAllData=true を使うと本番データが参照可能になります。ほとんどの場合、使用しないでください
  • Future/Batch/Queueable は Test.stopTest() の実行中ではなく、その後に実行されます
  • コールアウトのモック(Test.setMock())は Test.startTest() の前に登録する必要があります
  • テストではプラットフォームイベントの順序は保証されません
  • Test.startTest()/Test.stopTest() は各テストメソッドにつき 1 回しか呼び出せません
  • Batch Apex の finish() メソッドも Test.stopTest() の後に実行されます
  • Mixed DML は MIXED_DML_OPERATION をスローします。回避するには System.runAs() を使用してください

ワークフロー

  1. Read/Glob ツールを使ってテスト対象のクラスを読み込みます
  2. すべての public/global メソッドとコードパスを特定します
  3. TestDataFactory が存在するか確認し、なければ作成します
  4. すべてのシナリオタイプを含むテストクラスを生成します
  5. テストを実行します:sf apex run test -n MyClassTest --synchronous --code-coverage
  6. カバレッジを報告し、失敗があれば修正します

参考資料

  • テストパターン — 非同期テスト、プラットフォームイベント、CDC、Stub API、REST エンドポイント、Mixed DML、Flow のテストカバレッジ
  • ガバナ制限 — テストコンテキストにおけるトランザクションごとの制限

コメント