Apex開発でTest.isRunningTest処理を使ったメンテナンス性の向上についてです。
Apex開発を行ったときにはテストクラスの作成が必要になります。テストクラスをきちんと作成することで既存処理への予期せぬ影響に気づけたり、実装した箇所の考慮漏れや実装ミスに気づくことができます。
・・・ですが処理の内容によってはテストクラスが作成しずらい場面がでてきます。
たとえば下記のような処理です。
// 次回スケジュールバッチ実行時間取得 Datetime nextBatchTime = Datetime.now().addMinutes(5); // 次回スケジュールバッチ実行時間登録 OpportunityChatterPostBatchSchedule cls = new OpportunityChatterPostBatchSchedule(); String jobName = 'OpportunityChatterPostBatchScheduleJob_' + nextBatchTime.format('yyyyMMddHHmm'); String sch = nextBatchTime.format('0 m H d M ? yyyy'); // スケジュールバッチ登録 System.schedule(jobName, sch, cls);
上記の処理をそのままテスト実行すると下記のエラーが発生します。
System.AsyncException: "OpportunityChatterPostBatchScheduleJob_201909161032" という Apex ジョブはすでに実行がスケジュールされています。 3483
Class.OpportunityChatterPostBatch.finish: line 54, column 1
スケジュールバッチ処理実行時にジョブ名をユニークにするために日時を付与していますが、テストクラスで同じタイミングで複数呼び出しされることでユニークにならなくなってしまいました。こうした処理がある場合の対処方法として、テストクラスの実装を諦めるという手段もありますが、Test.isRunningTest処理を利用することできれいに解決できます。
Test.isRunningTest処理は「現在実行中のコードが、テストメソッドに含まれているコード」かを判別するための処理です。
これで下記のような書き方が可能となります。(if処理では「!」をつけてテストではない場合にという判定にできる)
if (!Test.isRunningTest()) { // テストでない場合にスケジュール登録処理を実行 System.schedule(jobName, sch, cls); }
スケジュール登録処理のみスキップできるようになりテストクラスを開発できるようになります。ですが上記の判定方法だとif判定内の処理は絶対に実行できないためカバー率低下が発生します。この問題を回避するために下記の記述にした方が良いと思います。
// スケジュールバッチ登録 String nextBatchJobId = (!Test.isRunningTest()) ? System.schedule(jobName, sch, cls) : '';
テストクラスの実行判定は行単位で識別されるのでこの書き方ならカバー率低下を回避することができます。また必要に応じてテスト処理の場合のみ任意も戻り値を指定することも可能になります。
このようにTest.isRunningTest()をうまく利用することでテスト実装ができない問題を回避しつつ正しくテストを行い品質とメンテナンス性を向上することができると思います。