14.3 Test Data Strategies & Isolation

Key Takeaways

  • By default tests do not see org data (SeeAllData=false); create the records each test needs
  • @TestSetup methods run once per test class to insert shared data visible to all test methods in that class
  • Test.loadData can load Static Resource CSV data; factories encapsulate complex object graphs in Apex
  • SeeAllData=true is discouraged—it couples tests to org data and breaks portability across sandboxes
  • Create Users carefully (unique UserName, required fields); isolation matters for parallel test execution and deterministic results
Last updated: August 2026

14.3 Test Data Strategies & Isolation

Quick Answer: Apex tests do not see existing org data by default (SeeAllData=false). Create what you need with DML, @TestSetup, Test.loadData, or factory helpers. Avoid SeeAllData=true except rare platform cases. Isolation keeps tests portable across sandboxes and safer under parallel execution.

Flaky tests usually fail because of data assumptions, not assertion syntax. Platform Developer I expects you to know the isolation model and the standard tools for building data.

Default Isolation: SeeAllData=false

Unless you opt in, test methods cannot query records that already exist in the org (standard and custom objects), with limited exceptions for some setup/metadata-like objects the platform exposes. That means:

  • A test that queries SELECT Id FROM Account LIMIT 1 without inserting Accounts first often returns zero rows.
  • Tests remain portable: they pass in empty scratch orgs and full sandboxes alike.
  • You must arrange records explicitly—which is good design.
@IsTest
static void findsOnlyTestInsertedAccounts() {
    // Org may have thousands of Accounts; this test still starts clean
    System.assertEquals(0, [SELECT COUNT() FROM Account]);
    insert new Account(Name = 'Only Mine');
    System.assertEquals(1, [SELECT COUNT() FROM Account]);
}

Each test method gets a rolled-back transaction for its data changes: inserts from one method do not leak into another method’s starting state (with the @TestSetup nuance below).

Why Isolation Matters

Risk without isolationSymptom
Depends on a named Account in UATFails in scratch org
Assumes a specific User or Queue IdFails after refresh
Counts “all Opportunities”Intermittent failures when parallel tests or other data exist
Uses production Id hard-codesInstant failure anywhere else

Isolation is also a parallelism concern: when suites run in parallel, tests that touch shared org data via SeeAllData=true can interfere with each other.

@TestSetup

@TestSetup methods (static, void, annotated) run once before the test methods in that class. Data they insert is available to every test method in the class, with each test method still getting a fresh view of that setup data as of setup completion (changes in one method do not permanently alter another method’s starting setup data).

@IsTest
private class CasePriorityHandlerTest {
    @TestSetup
    static void makeData() {
        List<Account> accounts = new List<Account>();
        for (Integer i = 0; i < 5; i++) {
            accounts.add(new Account(Name = 'Setup ' + i));
        }
        insert accounts;
    }

    @IsTest
    static void highPriority_setsSlaFlag() {
        Account a = [SELECT Id FROM Account WHERE Name = 'Setup 0' LIMIT 1];
        Case c = new Case(AccountId = a.Id, Status = 'New', Origin = 'Phone');
        // act + assert...
    }

    @IsTest
    static void bulkCases_succeed() {
        Account a = [SELECT Id FROM Account WHERE Name = 'Setup 1' LIMIT 1];
        // another scenario reusing setup Accounts
    }
}

Benefits: less duplicate arrange code, faster suites (shared insert cost amortized), clearer tests focused on the act/assert.

Limits: setup still must respect governor limits; do not load massive volumes. You cannot call Test.startTest in ways that confuse the model—keep setup to data creation.

Test.loadData

Test.loadData(sObjectType, staticResourceName) loads rows from a Static Resource CSV into test context:

@IsTest
static void loadsAccountsFromStaticResource() {
    List<sObject> accounts = Test.loadData(Account.SObjectType, 'Test_Accounts_CSV');
    System.assert(accounts.size() > 0, 'CSV should create Accounts');
}

Use loadData when:

  • Many fields/rows are tedious to build in Apex
  • Business users maintain sample CSVs
  • You want readable fixture files in source control (as static resources)

Prefer factories for logic-heavy graphs (Account → Contact → Opportunity with formulas and record types) where CSV columns become brittle.

Factory Patterns

A test data factory is an @IsTest or production-visible helper that builds valid sObjects:

@IsTest
public class TestDataFactory {
    public static Account makeAccount(String name, Boolean doInsert) {
        Account a = new Account(Name = name, BillingCountry = 'US');
        if (doInsert) insert a;
        return a;
    }

    public static List<Contact> makeContacts(Id accountId, Integer n, Boolean doInsert) {
        List<Contact> contacts = new List<Contact>();
        for (Integer i = 0; i < n; i++) {
            contacts.add(new Contact(
                AccountId = accountId,
                LastName = 'Person ' + i
            ));
        }
        if (doInsert) insert contacts;
        return contacts;
    }
}

Factories centralize required fields so when an admin adds a required custom field, you fix one place. Keep factories deterministic (no random names unless uniqueness requires a timestamp suffix).

SeeAllData=true — Discouraged

@IsTest(SeeAllData=true)
static void fragileTest() {
    Account a = [SELECT Id FROM Account LIMIT 1]; // depends on org contents
}

Why the exam and Salesforce best practices push back:

  • Non-deterministic: empty org → failure; full org → maybe wrong record
  • Security and privacy: tests may read real customer data in full sandboxes
  • Parallel and CI fragility
  • Hides missing arrange logic

Rare legitimate uses involve certain setup entities historically hard to create in tests—but for Platform Developer I scenarios (Accounts, custom objects, most configuration you can insert), create data explicitly. Prefer SeeAllData=false always unless you have a documented platform limitation.

You can set @IsTest(SeeAllData=true) on a class or method; do not treat it as a shortcut for laziness.

Creating Users, Groups, and Queues

Users require many fields and a unique Username (use a timestamp or GUID suffix). Inserting Users is allowed in tests without SeeAllData. Profiles are often queried ([SELECT Id FROM Profile WHERE Name = 'Standard User'])—Profile data is available as setup-type data in tests.

User u = new User(
    Alias = 'standt',
    Email = 'standarduser@example.com',
    EmailEncodingKey = 'UTF-8',
    LastName = 'Testing',
    LanguageLocaleKey = 'en_US',
    LocaleSidKey = 'en_US',
    ProfileId = [SELECT Id FROM Profile WHERE Name = 'Standard User'].Id,
    TimeZoneSidKey = 'America/Los_Angeles',
    UserName = 'standarduser' + DateTime.now().getTime() + '@example.com'
);
insert u;

Mixed DML caution: inserting setup objects (User, Group, Queue, etc.) and non-setup objects (Account, etc.) in the same transaction can throw mixed DML errors. Patterns:

  • Insert setup objects in one context and non-setup in System.runAs another user, or
  • Split with @future / Queueable in production code; in tests, structure arrange phases carefully

Queues and Groups: create when testing Case ownership or sharing, rather than assuming a queue named “Support” exists in every org.

Parallel Tests and Determinism

  • Avoid static variables that retain mutable state across tests in surprising ways (static reset behavior can confuse people—prefer instance data and clear patterns).
  • Do not depend on record order without ORDER BY.
  • Unique constraints (Username, external Id) need unique values per run.
  • Prefer asserting relative outcomes (this Account’s field) over org-wide counts when possible.

Practical Strategy Stack

  1. Default isolation (SeeAllData=false).
  2. @TestSetup for shared base records in the class.
  3. Factories for repeated graphs and required fields.
  4. Test.loadData for wide CSV fixtures when useful.
  5. runAs for user/sharing scenarios (section 14.2).
  6. Avoid SeeAllData=true.

Exam Checklist

  1. Tests do not see org data by default.
  2. @TestSetup builds shared per-class data efficiently.
  3. Test.loadData + static resource CSVs load bulk fixtures.
  4. Factories keep required-field construction DRY.
  5. SeeAllData=true is discouraged; create Users/queues carefully and mind mixed DML.

With solid data strategy, execution tools in 14.4 become straightforward.

Test Your Knowledge

What is the default visibility of existing org records to Apex unit tests?

A
B
C
D
Test Your Knowledge

What does an @TestSetup method provide in an Apex test class?

A
B
C
D
Test Your Knowledge

Why is @IsTest(SeeAllData=true) generally discouraged?

A
B
C
D