Apexクラスやトリガを作成した際は、それを検証するためのテストクラスを作成する必要がありますよね。
そして作成したテストクラスでは単体テストを実施することが可能です。
ただ、テストクラス作成やテスト実施に際し、注意することがいくつかあります。
そこで今回はApexテストクラスの作成と単体テストの実施方法について自分なりにまとめてみました。
参考程度に見ていってください。
Apexテストクラスとは
Apexテストクラスとは、作成したApexが正常に動くかどうかを確認するための機能です。
例えば、Apexを使用して取引先レコードを自動更新するトリガを作成したとします。Apexテストクラスは、このクラスのコードをテストし、バグやエラーを見つけるために使用されます。
品質を確保するという目的以外にも、Apexテストクラスには、Apexリリース時のコードカバー率要件を満たす(※次項で説明)、という役割があります。
テストを実行し、すべてのコードがカバーされている場合、コードカバー率は100%となります。
- 単体テストを通したApexクラスの品質確保
- リリース時のコードカバー率確保
Apexテストクラスの考慮事項
Apexテストクラスにはいくつか考慮事項がありますので、念頭に置いておきましょう。
- 本番リリースするには、Apex コードの少なくとも 75% が単体テストでカバーされており、かつすべてのテストが成功している必要がある。
- すべてのトリガについて何らかのテストを行う必要がある。
- System.debugへのコールは、Apex コードカバー率の対象とはみなされない。
- テストメソッド間ではトランザクションは保持されない。
- IsTest(SeeAllData=true)を使用すれば、組織の実データにアクセスできるが、価格表などの一部データは他のメソッドを使用する必要がある。
テストクラスで単体テストをするメリット
Apexテストクラスで単体テストを実施することで以下のようなメリットがあります。
- 手動でレコードをいちいち作成するしてテストする必要がない
- 維持管理がラク(バージョンアップの時など、テストを実行するだけで影響調査が可能)
もちろんコードカバー率を上げるためだけにテストクラスを作成して、単体テストは実際にレコードを作成して実施するというやり方でも正直なんとかなります(笑)
↑※Salesforce社非推奨です。
ただ、後々のエンハンス時やSalesforceのバージョンアップ時などに何かと便利なのでテストクラスで単体テストを実施しておく方がよいと思います。
テストクラスの作成
さて実際にテストクラスを作成して、テストを実行してみます。
ここでは以下のトリガをテストするテストクラスを作成します。
trigger Product2Trigger on Product2 (before insert) {
for(Product2 p : trigger.new){
if(String.isNotBlank(p.Name)){
if(p.Name == 'Strawberry'){
p.Color__c = 'Red';
}else if(p.Name == 'Banana'){
p.Color__c = 'Yellow';
}else{
p.Color__c = 'Other';
}
}
}
}
このトリガは、「商品」オブジェクトが作成された時に起動します。
作成された商品レコードの「Name」が”Strawberry”の場合、「Color」項目に”Red”を、
「Name」が”Banana”の場合、「Color」項目に”Yellow”を、
「Name」がそれ以外の場合、「Color」項目に”Other”を設定する処理です。
上のトリガのテストクラスは以下のように作成します。
@isTest(SeeAllData=false) /** 1 **/
public class Product2TriggerTest {
@isTest /** 2 **/
static void test01(){
//Create test data
Product2 p = new Product2(Name = 'Strawberry');
/** 3 **/
Test.startTest();
Insert p;
Test.stopTest();
//Check result
Product2 result = [Select Id, Name, Color__c From Product2 Limit 1];
/** 4 **/
Assert.areEqual(result.Name, 'Strawberry');
Assert.areEqual(result.Color__c, 'Red');
}
@isTest
static void test02(){
//Create test data
List<Product2> pList = new List<Product2>();
pList.add(new Product2(Name = 'Strawberry'));
pList.add(new Product2(Name = 'Banana'));
pList.add(new Product2(Name = 'Orange'));
Test.startTest();
Insert pList;
Test.stopTest();
//Check result
List<Product2> results = [Select Id, Name, Color__c From Product2];
/** 5 **/
Assert.areEqual(3, results.size());
Boolean case1 = false;
Boolean case2 = false;
Boolean case3 = false;
for(Product2 p : results){
if(p.Name == 'Strawberry'){
case1 = true;
Assert.areEqual('Red', p.Color__c);
}else if(p.Name == 'Banana'){
case2 = true;
Assert.areEqual('Yellow', p.Color__c);
}else if(p.Name == 'Orange'){
case3 = true;
Assert.areEqual('Other', p.Color__c,);
}
}
/** 6 **/
Assert.isTrue(case1 && case2 && case3);
}
/** 7 **/
@testSetUp
static void testSetUp(){
//Create test data
}
}
※上記コードのコメントアウト番号と対応
① テストクラスの先頭には「@isTest」を記述。「(SeeAllData=false」と記述するとテスト実行時に組織データにアクセス可能。記載を省略するとデフォルトでfalseとなる。(予期せぬデータ更新につながる可能性があるため使用時は注意)
② テストメソッドの先頭にも「@isTest」を記述。
③ startTestメソッドとstopTestを使用し、テストコード内のテストが実際に開始、終了するポイントをマーク。startTestへのコールの後および stopTestの前に実行するコードはすべて、新しいガバナ制限セットが割り当てられる。
つまり、確認したい処理がガバナ制限にに底触するかを確認できる。
④ トリガ処理により正しくレコードが処理されたかを確認。AssertクラスのareEqualメソッドを使用。第一引数に期待値、第二引数に実行結果を渡し、両値を比較。一致しない場合はテスト失敗。
⑤ 複数レコードを処理する場合は、処理レコード数を確認。
⑥ if分岐に入らずAssertがスルーされて成功してしまうケースを排除するため Boolean変数を用意し、すべてのif文を通っているかを確認。Assert.IsTrueですべてのBoolean変数がTrueかどうかを判定。
⑦ テストクラス内で共通するテストデータを使用する際に使用。@testSetUpで作成したデータは同じテストクラス内のどのメソッドからでもSOQLで取得、使用が可能。
テストの実行
続いてテストの実行方法です。
開発者コンソールの上部にある「Test」> 「New Run」を選択します。
テストクラスを選択し、実行したいテストメソッド名にチェックを入れ「Run」を押下します。
実行結果は画面下部の「Tests」タブに表示されます。
テストが成功した場合は、上画像のようにチェックマークが付きます。
失敗した場合は、×マークが表示され失敗した箇所の詳細を確認できます。
また、テストを実行したいテストクラスを開き、画面右上の「Run Test」を押下することでそのテストクラスのすべてのテストメソッドを実行することが可能です。
テストクラスに関する記事
その他のテストクラスに関することは以下の記事にもまとめていますので良ければ覗いてみてください。
まとめ
今回はApexテストクラスの作成と単体テストの実施方法についてでした。
作成についてはいくつかのお決まりごとがあったり、結果の確認方法など機能によって工夫して作成する必要があり、最初は少し手間に感じるかもしれません。
ただ、一度作成してしまえば、その後に改修があった場合でも対応しやすかったりするのでメリットはあるのかなと。
いちいち画面からレコードを作成するのも面倒ですしね。
こんな感じで今回は以上です。
コメント