← Back to notes

Developers · Tutorial

System.LimitException: Too Many SOQL Queries: 101 — Testing Apex Governor Limits

Diagnose “Too Many SOQL Queries: 101” with a debug-log experiment showing how @TestSetup and Test.startTest() create distinct SOQL governor-limit contexts in Apex tests.

System.LimitException: Too Many SOQL Queries: 101 — Testing Apex Governor Limits

In a normal synchronous Apex transaction, the SOQL limit is 100 queries. Tests add an important detail: Test.startTest() opens a fresh governor-limit context and Test.stopTest() restores the context that was active before it. When that is combined with @TestSetup, a test run can execute work in three distinct SOQL contexts.

The important distinction: this is not a way to give one transaction, trigger, or production method 300 SOQL queries. It is a testing behavior. Production code must still be bulkified and must not query in loops.

Complete test class

The expected value of Limits.getQueries() is written beside every step. A, B and C are visual labels for the experiment.

@IsTest
private class UnitTestSoqlLimits {
    @TestSetup
    static void testSetup() {
        // Context A (blue): 0
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList1 = [SELECT Id FROM Account];
        // Context A (blue): 1
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());

        Test.startTest();
        // Context B (violet): 0
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList2 = [SELECT Id FROM Account];
        // Context B (violet): 1
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        Test.stopTest();

        // Context A (blue): 1
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList3 = [SELECT Id FROM Account];
        // Context A (blue): 2
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
    }

    @IsTest
    static void firstTestMethod() {
        // Context A (blue): 2
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList4 = [SELECT Id FROM Account];
        // Context A (blue): 3
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());

        Test.startTest();
        // Context C (green): 0
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList5 = [SELECT Id FROM Account];
        // Context C (green): 1
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        Test.stopTest();

        // Context A (blue): 3
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList6 = [SELECT Id FROM Account];
        // Context A (blue): 4
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
    }

    @IsTest
    static void secondTestMethod() {
        // Context A (blue): 2
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList7 = [SELECT Id FROM Account];
        // Context A (blue): 3
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());

        Test.startTest();
        // Context C (green): 0
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList8 = [SELECT Id FROM Account];
        // Context C (green): 1
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        Test.stopTest();

        // Context A (blue): 3
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
        List<Account> accountsList9 = [SELECT Id FROM Account];
        // Context A (blue): 4
        System.debug('Number of SOQL queries executed up to this point: ' + Limits.getQueries());
    }
}
Apex debug log evidence showing the SOQL counters for the setup and each test method
Evidence extracted from the Apex log: each test starts context A at 2, starts context C at 0, and resumes context A at 3.

What the log proves

Context A is the original governor-limit context. The two queries made in @TestSetup become the starting point for each test method. Each method receives its own copy: both begin at 2, rather than the second continuing from the first method.

Context B is created by Test.startTest() in the setup method. Context C is created by the same call inside each test method. Both begin at 0, and Test.stopTest() restores the prior context.

Why people describe this as “300 SOQL queries”

There can be up to 100 queries in A, up to 100 in B, and up to 100 in C. That is 300 queries across setup and one test method in separate governor-limit contexts—not a 300-query allowance for production code.

How to use this safely

  • Put reusable records in @TestSetup, but keep it focused on fixture creation.
  • Call Test.startTest() immediately before the operation you actually want to test, then assert after Test.stopTest().
  • Do not use the extra test contexts to excuse SOQL in loops or an inefficient service. The code under test must still respect the normal 100-query transaction limit.
  • Use Limits.getQueries() while diagnosing tests, not as a substitute for assertions about business behavior.

Salesforce documents that startTest() creates a new set of governor limits and stopTest() returns to the previous one. It also recommends using the pair to isolate test setup from the behavior under test. See the Salesforce testing guidance and its Apex best-practices explanation.