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.)

1 Answer 1

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .