プログラマ38の日記

主にプログラムメモです。

Salesforce: テストクラスの作成での留意事項

SalesforceのApex開発を行う場合、本番環境にリリースするためにテストクラスを作成する必要があります。

 

設定とちょっとのトリガなどの開発であれば、テストクラスの作成はさほど大変ではないのですが、サイトやコミュニティで多くの画面を作りこむ場合、テストクラスの作成に多くの労力を必要とします。

さらに、モジュールのリリースの際のテストクラス実行中の待ち時間も意外とかかるのでテストクラスの作成の仕方は工夫が必要だと感じています。

 

個人的にテストクラスの作成で思うことは次になります。

  1. テストクラスでは最小メソッド単位の呼び出しをメインとする
  2. テストデータ登録時のトリガは極力スキップする仕組みを作る

1. テストクラスでは最小メソッド単位の呼び出しをメインとする。

テストクラスは大体以下のようになると思います。

  • カスタムWebServiceの場合メソッドを実行
  • トリガの場合、あらかじめ必要なマスタを用意した後、対象のオブジェクトへレコードを作成、更新、削除を実行
  • コントローラの場合、Visualforceページで必要なパラメータを指定後、コントローラのメソッドを実行

上記のテストメソッドは必要ですが、全てのテストメソッドを上記の書き方で書く必要はないと考えています。(労力もかかりますし、テストメソッドの実行時間も長くなります)

通常、上記のメソッドの中はさらに細かいメソッドで構成されています。テストクラスでは、その細かいメソッド単位で実行するようにします。

細かいメソッドは、そのクラス内でしかアクセスしないためprivate や protected としていると思いますが、Salesforceではテストクラスからのテスト用に テストビジブルアノテーション(@TestVisible)が用意されているので、これを使っていきます。

 

テストクラスでカバレージを上げるポイントとして、通常クラスの細かいメソッド内で業務ロジックを記載する場合、SOQLを発行しないようにしておきます。(SOQLを発行するメソッドはSOQLの発行だけの機能とします)

 

コントローラクラスの作成例は次の通りです。

public class SampleController {

    public Case caseObj {get; set;}
    String caseId;
    
    public APC_CUS_200_ShinchokuDetailCtrl(){
      caseId =  ApexPages.currentPage().getParameters().get('caseId');
    }
    
    //vf action
    public void doinit(){
      caseObj = [Select Id  ,Type ,Origin ,Subject ,Priority ,ParentId ,AccountId
                 From Case where id = :caseId ]; 
    }

    public PageReference doSomething() {
      Account acc        = getAccount();
      Case    parentcase = getParentCase();
      
      doCheck(acc, parentcase);
    }
    
    private Account getAccount(){
      if( caseObj.AccountId == nul ){
        return null;
      } else {
          return [Select  Id ,Name ,Industry 
From Account where ID =:caseObj.AccountId][0];
      }
    }

    private Case getParentCase(){
      if( caseObj.ParentId == nul ){
        return null;
      } else {
          return [Select  Id  ,Type ,Origin ,Subject ,Priority 
From Case where ID =:caseObj.ParentId][0];
      }
    }
    
    @TestVisible
    private void doCheck(Account acc,Case parentcase){
    
      if( caseObj.Origin != parentcase.Origin ){
        ・・・・・
      }
    
      if( acc.Industry == 'Education' && parentcase.Origin == 'Phone' ){
        ・・・・・
      }
    }
}

そして上記のdoCheckのメソッドのテストクラスは次のようにします。

@isTest
private class SampleControllerTest {

  static testMethod void testMethod01() {
    SampleController ctrl = new SampleController();
    
    Case caseobj    = new Case();
    Case parentcase = new Case();
    Account acc     = new Account();
//必要なマスタの値のセットをする。
    
    ctrl.caseObj = caseobj;
    ctrl.doCheck(acc, parentcase);
  }
}

こうすることで、特にマスタを事前に登録しておくことも不要となり、doCheckの業務ロジックを確実にテストすることができ、カバレージも確保できます。

 

カスタムWebサービスや、トリガでも同様に、カスタムWebサービスのクラスやトリガハンドラークラスの細かいメソッドを呼び出すことで対応します。

2. テストデータ登録時のトリガは極力スキップする仕組みを作る

上記1ではデータを作成せずにテストをすることで、効率をあげるということを書いていますが、テストクラスでテストデータの作成は避けて通ることができません。

ですが、テストクラス用のデータを作成するときにそのオブジェクトのトリガが邪魔になるときがあります。
そのトリガ処理のテストをしたいわけではないのに、トリガのエラーを解決するために時間がかかったりして効率が落ちます。

そこで、データ登録時のトリガをスキップする処理を用意しておくのはどうでしょうか。そこまで大した変更をせずにテストで使いたいデータを登録ができて、本来のテストに集中することができます。(もちろん全てのオブジェクトのトリガにスキップを入れる必要はありません、効果的な箇所だけ対応するのが良いと思います)
トリガのスキップについては以前に書いていますのでそちらを参照ください。