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.
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.
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());
}
}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 afterTest.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.