Apexテストクラスのベストプラクティスおよびテンプレート

By | October 9, 2021

ベストプラクティス

■汎用的なテストデータは@isTest内で準備する

@testSetupで準備したデータは全てのテストメソッドで使いまわすことができます。

そのため、各テストメソッドで共有して使いまわしたいテストデータは、各テストメソッド内ではなく、@testSetupアノテーション付きのメソッド内で事前に準備しておきましょう。

■テストデータの作成は、TestDataFactoryを利用する

TestDataFactoryを利用することで、オブジェクトのメタデータに依存する記述を一箇所に集約できるようになります。

これにより、必須項目の追加や入力規則の追加などがあった場合も、個々のApexを修正しなくともTestDataFactoryだけを変更すればようになり、メンテナンスコストが大幅に削減されます。

■(各メソッドに固有の)テストデータの準備はTest.startTest()よりも前に終わらせておく

Test.startTest()とTest.stopTest()の中は、その外とは別のガバナ制限のコンテクストにおいて動作します。

実際の環境ではデータが事前準備されている(=テストデータ挿入のDML操作がテストに影響を及ぼしてはいけない)ことを考慮すると、Test.startTest()よりも前の段階でテストデータ作成が終了していることが望ましいです。

■System.runAs()を利用する

System.runAs()を利用することで、特定のプロファイルやユーザとしてテストメソッドを実行することが可能です。

常にシステムコンテクストで動作するロジックでない限り、System.runAs()を利用してアクセス権までチェックしましょう。

■AssertをTest.stopTest()よりも後に記述する

非同期処理はTest.stopTest()の次の行よりも前に必ず終了します。

言い換えると、Test.stopTest()には非同期処理を同期処理化する意味合いがあります。

処理結果を検証するはずのAssertが、処理の途中で動作しては全く意味がありませんので、Assertは必ずTest.stopTest()よりも後に記述するようにしましょう。

■クラスとメソッドはそれぞれ下記の規則に従って命名する。

クラス名:テスト対象クラス+Test

  • Bad:Test_AccountTrigger
  • Good:AccountTrigger_Test

メソッド名:テスト対象メソッド名_シナリオ_期待される動作

  • Bad:TestMethod1
  • Good:createAccount_DuplicateAccountId_ReturnsDuplicateError

命名規則に関する参考リンク↓

=======TestClassのテンプレート↓=======

TestClass

/*******************************************************************************************
* @CopyRight    Eherenfest. Inc.
* @CreatedDate  2021/09/01
* @Description  テストクラスforAccountTrigger&AccountTriggerHandler
* @Author      りがるでぃ
* @Modification Log
*******************************************************************************************/

@isTest
private class AccountTriggerTest{

    @testSetup
    static void setup() {
        //テストデータ準備(※各メソッドに共通のデータを準備)
        Account testAccount = TestDataFactory.createAccount(true);
        List<Opportunity> oppList = TestDataFactory.createOpportunity(acc.id,5);
        insert oppList;
    }

    @isTest
    static void testMethod() {
        
    //テストデータ準備(※このメソッドに特有のデータを準備)
        Account accToUpdate = [SELECT Id,Name FROM Account LIMIT 1];
        accToUpdate.name = 'にゃん';
        update accToUpdate;

        Test.startTest();
      System.runAs(new User(ID = UserInfo.getUserID())) {
          //テストメソッド実行
      }
        Test.stopTest();

      //結果検証
       List<Opportunity> oppListToCheck = [SELECT Id FROM Opportunity];
       System.assertEquals(oppListToCheck.size(),5)
    }
}

TestDataFactory

/*******************************************************************************************
* @CopyRight    Eherenfest. Inc.
* @CreatedDate  2021/09/01
* @Description  TestDataFactory
* @Author      りがるでぃ
* @Modification Log
*******************************************************************************************/

@isTest
public with sharing class TestDataFactory{

  //リード
    public static Lead createLead(Boolean doInsert){
        Lead newLead = new Lead() ;
        newLead.FirstName = 'Taro';
        newLead.LastName = 'Yamada';
        newLead.Company = 'TestCompany';
        if(doInsert){
            insert newLead;
        }
        return newLead;
    }
  
  //取引先
    public static Account createAccount(Boolean doInsert){
        Account acc = new Account();
        acc.Name = 'TestAccount';
        if(doInsert){
            insert acc;
        }
        return acc;
    }
  
  //商談(複数)
  public static List<Opportunity>createOpportunity(Id accountId, Integer numOpps) {
		List<Opportunity> opps = new List<Opportunity>();
		for(Integer i = 1; i <= numOpps; i++) {
			Opportunity opp = new Opportunity();
			opp.name = 'Account ' + i;
			opp.accountId = accountid;
			opp.amount = 500;
			opp.closeDate = Date.today().addDays(10);
			opp.stageName = 'Prospecting';
			opps.add(opp);
		}
		return opps;
	}
  }
}

TestDataFactory関連のリンク↓

https://github.com/benahm/TestDataFactory

https://github.com/amiller-smg3/Salesforce-Test-Data-Factory

https://github.com/nilvon9wo/ExtremeApexTestDataFactory