Monitor governor limits in Apex tests

apexgovernorlimitsunit-test

We have been getting surprise random failures in our legacy Apex unit tests when certain types of modifications are made to process automation logic (usually new DML in triggers, flows, etc). I'm looking for ways to regularly monitor which Apex tests are closest to hitting SOQL and DML governor limits, so we can avoid these surprises.

Debug logs include information about usage vs. limits, but reading thousands of debug logs manually is not practical or repeatable. Do I have any other options?

Best Answer

As I was reading @sfdcfox's answer and considering why I felt it missed the mark a bit, I realized there's an approach that would meet my needs better.

The idea would be to create a test utility method that can be invoked from every test. A Boolean value in the utility (in my example, doMonitoring) can be toggled to force every test to fail artificially, and in the failure message, we report out on the usage stats.

Sample utility class:

@isTest
public class TestLimitsUtil {

    // Toggle this when monitoring usage vs. governor limits.
    // Could be custom metadata if you would rather have it configurable.
    private static final Boolean doMonitoring = false;
    private static String fullMessage = '';

    public static void monitorInnerTestLimits() {
        if (doMonitoring) {
            fullMessage = 'INNER TEST USAGE\n' + getLimitsMessage();
        }
    }

    public static void monitorOuterTestLimits() {
        if (doMonitoring) {
            fullMessage += '\n\nOUTER TEST USAGE\n' + getLimitsMessage();
            Assert.fail(fullMessage);
        }
    }

    private static String getLimitsMessage() {
        String message = 'CPU time: ' + Limits.getCpuTime();
        message += '\nDML statements: ' + Limits.getDmlStatements();
        message += '\nDML rows: ' + Limits.getDmlRows();
        message += '\nSOQL queries: ' + Limits.getQueries();
        message += '\nSOQL query rows: ' + Limits.getQueryRows();
        return message;
    }
}

Sample usage

@isTest
static void myUnitTest() {

    // SOME PRE-TEST LOGIC, QUERIES, ETC

    Test.startTest();

    // SOME INNER TEST LOGIC

    TestLimitsUtil.monitorInnerTestLimits();
    Test.stopTest();

    // SOME POST-TEST QUERIES, ASSERTS, ETC

    TestLimitsUtil.monitorOuterTestLimits();
}

I see the following advantages of this approach over @sfdcfox's:

  • You get usage statistics for every test, not just the ones that are in the danger zone
  • You get usage stats for the separate set of governor limits inside Test.startTest();/Test.stopTest(); as well as those outside
  • You can keep it toggled off most of the time, so it doesn't block production deployments