4

I have an app with a quite complex data model and many asynch batch jobs triggered by DML operations on many of my custom objects.

As in Apex Test you can only synchronize a single asynch operation with Test.startTest and Test.stopTest I find myself basically not beeing able to even write a single test method as I will fire batch jobs already in the setup part of my test methods.

Any idea how I could cope with that? (Rewriting my batches in a way that they run synchronously in test scope is something I thought of but its way to complicated to rewrite the code.)

5

Here is one approach to consider where for tests the code is run synchronously so Test.stopTest calls are not needed.

Wherever you invoke:

Database.executeBatch

replace that code with:

BatchableExecutor.executeBatch

that is a local class that looks like this:

public class BatchableExecutor {
    private static String KEY_PREFIX {
        get {
            if (KEY_PREFIX == null) {
                KEY_PREFIX = AsyncApexJob.SObjectType.getDescribe().getKeyPrefix();
            }
            return KEY_PREFIX;
        }
        set;
    }
    public static Id executeBatch(Database.Batchable<SObject> batchable, Integer scopeSize) {
        if (!Test.IsRunningTest()) {
            return Database.executeBatch(batchable, scopeSize);
        } else {
            return executeBatchSynchronously(batchable, scopeSize);
        }
    }
    private static Id executeBatchSynchronously(Database.Batchable<SObject> batchable, Integer scopeSize) {
        // Fake implementation of this interface could be added as neeed
        Database.BatchableContext bc = null;
        // Invoke start (assumes QueryLocator is being used)
        Database.QueryLocator start = (Database.QueryLocator) batchable.start(bc);
        Database.QueryLocatorIterator iter = start.iterator();
        List<SObject> sobs = new List<SObject>();
        try {
            // Invoke execute
            while(iter.hasNext()) {
                sobs.add(iter.next());
                if (sobs.size() == scopeSize) {
                    // These calls could be wrapped in try/catch too for negative tests
                    batchable.execute(bc, sobs);
                    sobs.clear();
                }
            }
            if (sobs.size() > 0) {
                batchable.execute(bc, sobs);
            }
        } finally {
            // Invoke finish
            batchable.finish(bc);
        }
        // Fake id
        return KEY_PREFIX + '000000000000';
    }
}

The class does a basic emulation of the batching.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.