バッチapexを実行するスケジュール可能なクラスをテストすると、一貫性のない結果が得られます

Aug 19 2020
@isTest(seeAllData = false)
private class interfacetest {

    public static testMethod void testInterfaceScheduler(){
        Product2 testproduct = new Product2(Name='test product',ProductCode = '112233');
        insert testproduct;
        Test.startTest();
        InterfaceCalloutMock fakeResponse = new InterfaceCalloutMock(200);
        Test.setMock(HttpCalloutMock.class, fakeResponse);
        InterfaceSchedule InterfaceSc = new InterfaceSchedule();
        String sch = '0 0 23 * * ?';
        system.schedule('Test Interface Scheduler', sch, InterfaceSc);
        Test.stopTest();
        List<Product_Image__c> images = [select Id from Product_Image__c where Product__c =:testproduct.Id];
        system.assertEquals(1, images.size());
    }

    public static testMethod void testInterfaceBatch(){
        Product2 testproduct = new Product2(Name='test product',ProductCode = '112233');
        insert testproduct;
        Test.startTest();
        InterfaceCalloutMock fakeResponse = new InterfaceCalloutMock(200);
        Test.setMock(HttpCalloutMock.class, fakeResponse);
        InterfaceBatch Batchtest = new InterfaceBatch();
        database.executebatch(Batchtest,100);
        Test.stopTest();
        List<Product_Image__c> images = [select Id from Product_Image__c where Product__c =:testproduct.Id];
        system.assertEquals(1, images.size());
    }

    public class InterfaceCalloutMock implements HttpCalloutMock {
        Integer responseCode {get;set;}
        InterfaceCalloutMock(Integer responseCode){
            this.responseCode = responseCode;
        }
        public HTTPResponse respond(HTTPRequest req) {
            HttpResponse resp = new HttpResponse();
            resp.setStatusCode(responseCode);
            resp.setBody('[{"id":"31A3FCE7-DDEF-40D1-8365A8CAA8809348"}]');
            return resp;
        }
    }

} 

スケジュール可能なクラスは、executeメソッドでバッチ頂点を実行するだけです。

何らかの理由で、testInterfaceSchedulerはアサーションに失敗しますが、testInterfaceBatchはアサーションに合格します。理由がわかりません。助けていただければ幸いです。

ありがとうございました!

回答

1 cropredy Aug 19 2020 at 18:13

これは、testInterfaceScheduler後に2つの非同期トランザクションを実行しようとしているためです。Test.stoptest()

  • スケジュール可能
  • バッチ処理可能

Test.stopTest()は、assertステートメントに進む前に最初の非同期トランザクションのみを実行します。デバッグログには、実行中のバッチ可能オブジェクトが表示されますが、実際には(テストコンテキストで)アサート後に発生します。

さて、これを修正する方法は、適切なユニットテストを行うことです

  • スケジュール可能なものをテストするとき。コンストラクターとexecute()メソッドをテストするだけです。あなたが本当に気にしているのは、execute()がバッチ可能を開始したことです。また、バッチ可能なものにAsyncApexJobがあることを簡単に確認できます。バッチ可能が開始することをテストする必要はありません。
  • バッチ可能オブジェクトをテストするときは、start()を見つけるためのモックオブジェクトを提供し、検証しexecute()finish()必要な処理を実行します

コンストラクター引数をバッチ可能に渡す場合は、次のようにして、バッチ可能が適切な引数で呼び出されていることを確認できます。

@IsTest static void testBatchable() {
   InterfaceSchedule schedulable = new InterfaceSchedulable(args);
   Test.startTest();
   schedulable.execute(null);  // SchedulableContext can't be constructed
   Test.stoptest();  // batchable will execute now
   // do asserts

}

Schedulableexecute()は次のようになります

public void execute(SchedulableContext sc) {
  InterfaceBatch batchable = new InterfaceBatch(this.args);  // from Dependency-injected constructor args  
  Database.executeBatch(batchable);
}    

つまり、依存性は、バッチ可能に渡したいいくつかの引数をスケジュール可能に注入します。これは、スケジュール可能なコンストラクターにargsなしのコンストラクターと「argsあり」のコンストラクターがあることを意味します。