0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Apexテストのベストプラクティス

Last updated at Posted at 2025-02-25

Apexのテストは、本番環境でのバグを防ぐ ために欠かせません。
適切なテストを書くことで、アプリケーションの品質を向上 させることができます。
今回は、4つのベストプラクティス について、具体的なコード例 を使って整理しました。


1. データ条件のテストをする(有効、無効、一括など)

✅ 理由

  • 本番環境では様々なデータパターンが存在するため、すべてのケースを考慮したテストが必要。
  • 有効なデータ(正常系)、無効なデータ(異常系)、一括データ(大量処理)をテストすることで、エラーを未然に防げる。

📌 例:コース開催 (Course_Delivery__c) の Start_Date__c のテスト

@isTest
private class CourseDeliveryTest {

    // ✅ 1. 正常なデータ(有効な開始日)
    @isTest
    private static void testValidCourseDelivery() {
        Course_Delivery__c courseDelivery = new Course_Delivery__c(
            Name = 'Valid Course',
            Start_Date__c = Date.today().addDays(10) // ✅ 未来の日付(正常)
        );

        Test.startTest();
        Database.SaveResult result = Database.insert(courseDelivery, false);
        Test.stopTest();

        System.assertEquals(true, result.isSuccess(), '正常なデータは登録できるはず');
    }

    // ❌ 2. 無効なデータ(過去の日付でエラー)
    @isTest
    private static void testInvalidCourseDelivery() {
        Course_Delivery__c courseDelivery = new Course_Delivery__c(
            Name = 'Invalid Course',
            Start_Date__c = Date.today().addDays(-1) // ❌ 過去の日付(エラー)
        );

        Test.startTest();
        Database.SaveResult result = Database.insert(courseDelivery, false);
        Test.stopTest();

        System.assertEquals(false, result.isSuccess(), '過去の日付のコースは登録できないはず');
    }

    // 📦 3. 一括データのテスト(10件登録)
    @isTest
    private static void testBulkInsertCourseDeliveries() {
        List<Course_Delivery__c> courses = new List<Course_Delivery__c>();
        for (Integer i = 0; i < 10; i++) {
            courses.add(new Course_Delivery__c(
                Name = 'Bulk Course ' + i,
                Start_Date__c = Date.today().addDays(i + 1) // ✅ すべて未来の日付
            ));
        }

        Test.startTest();
        Database.SaveResult[] results = Database.insert(courses, false);
        Test.stopTest();

        for (Database.SaveResult result : results) {
            System.assertEquals(true, result.isSuccess(), 'すべてのデータは正常に登録されるはず');
        }
    }
}

正常系、異常系、大量データ処理のすべてをテストすることで、バグを未然に防げる!


2. クラスとテストクラスは1対1

✅ 理由

  • クラスごとに対応するテストクラスを用意すると、管理がしやすくなる。
  • どのテストがどのクラスを検証しているのかが明確になる。

📌 ❌ 悪い例(複数の対象クラスを1つのテストクラスで扱う)

@isTest
private class AllTests {
    @isTest private static void testCourseDelivery() { /* ... */ }
    @isTest private static void testOtherFunctionality() { /* ... */ }
}

問題点

  • どのクラスのテストなのか不明確 で、コードが煩雑になる。

📌 ✅ 良い例(1つのクラスに対して1つのテストクラス)

@isTest
private class CourseDeliveryTest {
    @isTest private static void testValidCourseDelivery() { /* ... */ }
}

対応関係が明確になり、管理しやすくなる!


3. テストデータが適切に作成されているか

✅ 理由

  • 本番環境とSandbox環境ではレコードIDが異なるため、IDをハードコードするとエラーの原因になる。
  • テストメソッド内でデータを作成することで、環境に依存しないテストが可能。

📌 ❌ 悪い例(IDをハードコードしている)

@isTest
private class BadTestExample {
    @isTest
    private static void testWithHardcodedId() {
        Course_Delivery__c course = [SELECT Id FROM Course_Delivery__c WHERE Id = 'a0123456789XYZ']; // ❌ IDをハードコード
        System.assertNotEquals(null, course);
    }
}

問題点

  • 本番環境とSandboxでIDが異なるため、テストが失敗する可能性がある。

📌 ✅ 良い例(テストデータを動的に作成)

@isTest
private class GoodTestExample {
    @isTest
    private static void testWithDynamicallyCreatedData() {
        Course_Delivery__c course = new Course_Delivery__c(Name = 'Test Course', Start_Date__c = Date.today().addDays(5));
        insert course;

        Course_Delivery__c retrieved = [SELECT Id FROM Course_Delivery__c WHERE Id = :course.Id];
        System.assertNotEquals(null, retrieved, 'コースが存在するはず');
    }
}

環境に依存しない、安定したテストコードになる!


4. SOSL検索のテスト

✅ 理由

  • SOSLの検索結果はテスト環境では通常空になるため、明示的に結果をセットする必要がある。
  • Test.setFixedSearchResults() を使用して、テストデータの検索結果を指定する。

📌 ✅ 良い例(Test.setFixedSearchResults() を使用)

@isTest
private class SearchHelperTest {

    @testSetup
    private static void setupTestData() {
        Contact contact1 = new Contact(FirstName = 'Taro', LastName = 'Yamada', Email = 'taro@example.com');
        insert contact1;
    }

    @isTest
    private static void testSOSLSearch() {
        // 検索結果として返したいレコードのIDをリストに格納
        List<Id> expectedIds = new List<Id>();

        List<Contact> contacts = [SELECT Id FROM Contact WHERE LastName = 'Yamada'];
        if (!contacts.isEmpty()) {
            expectedIds.add(contacts[0].Id);
        }

        // SOSLの検索結果を固定
        Test.setFixedSearchResults(expectedIds);

        Test.startTest();
        List<sObject> results = [FIND 'anytest' IN ALL FIELDS RETURNING Contact(Id, Name)];
        Test.stopTest();

        System.assertEquals(expectedIds.size(), results.size(), '検索結果の件数が一致するはず');
    }
}

テスト環境でも確実にSOSLの検索結果を取得できるようになる!


🎯 まとめ

ベストプラクティス 理由 具体例
1. データ条件のテスト 正常系・異常系・一括処理を確認 未来/過去の日付のバリデーションをテスト
2. クラスとテストクラスを1対1 テストの管理がしやすい CourseDeliveryTest を作成
3. レコードIDをハードコードしない 環境に依存せずにテストできる insert でデータを作成
4. SOSL検索のテスト Test.setFixedSearchResults() を使用 確実に検索結果を取得
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?