Study guide

14.3 Test Data Strategies & Isolation

Salesforce Certified Platform Developer I Exam

Track your progress as you read

Finished sections get a check mark.

1 Introduction & Exam Overview

1.1 Current Exam Facts 1.2 Blueprint Weights & Study Strategy

2 Platform Fundamentals

2.1 Multi-Tenant Architecture & Shared Resources 2.2 MVC Mapping on the Lightning Platform 2.3 Declarative vs Programmatic Decisions 2.4 Agentforce for Developers: Use Cases & Limitations

3 Data Modeling & Schema

3.1 Objects, Fields & Schema Design 3.2 Relationship Types & Implications 3.3 Schema Design Impact on Apex 3.4 Data Import & Export for Development

4 Platform Data Features

4.1 Formula Fields & Roll-Up Summaries 4.2 Core CRM Objects & Standard Schema 4.3 AppExchange Extensions & Platform Boundaries

5 Declarative Process Automation

5.1 Record-Triggered Flows & Declarative Automation 5.2 Approval Processes & Business Rules 5.3 When to Use Apex vs Declarative Tools

6 Apex Language Basics

6.1 Variables, Constants & Apex Data Types 6.2 Lists, Sets & Maps 6.3 Control Flow & Exception Handling 6.4 Classes, Interfaces & OOP Patterns

7 SOQL, SOSL & DML

7.1 SOQL Query Fundamentals 7.2 SOSL Search Fundamentals 7.3 DML Operations & Bulkification 7.4 Governor Limits & Transaction Boundaries

8 Apex Triggers & Order of Execution

8.1 Trigger Structure & Context Variables 8.2 Bulk Trigger Patterns & Best Practices 8.3 Save Order of Execution 8.4 Recursion, Cascading & Transaction Control

9 Asynchronous Apex

9.1 @future Methods 9.2 Queueable Apex 9.3 Batch Apex 9.4 Schedulable Apex & Job Orchestration

10 Security, Events & Schema Access

10.1 Sharing, FLS, CRUD & Injection Prevention 10.2 Platform Events Publish & Subscribe 10.3 Programmatic Schema Access (Describe)

11 Visualforce

11.1 Visualforce Pages & Markup 11.2 Standard, Custom Controllers & Extensions 11.3 Visualforce in Lightning Experience

12 Lightning Components

12.1 Lightning Component Framework Benefits 12.2 Aura Component Structure & Resources 12.3 Lightning Web Components Fundamentals 12.4 LWC Data Access: Wire, Imperative & Events

13 UI Composition & Apex Integration Patterns

13.1 Composing UI in Lightning Apps 13.2 UI Security, Performance & Accessibility Patterns 13.3 Apex Actions for Flow, LWC, and Agentforce

14 Apex Testing

14.1 Testing Framework & Coverage Requirements 14.2 Writing Unit Tests for Triggers & Classes 14.3 Test Data Strategies & Isolation 14.4 Executing Tests & Execute Anonymous Differences

15 Debugging & Developer Tools

15.1 Debug Logs & Monitoring 15.2 Developer Console, CLI, VS Code & Workbench 15.3 Sandboxes, Scratch Orgs & Environment Strategy

16 Deployment & Release

16.1 Change Sets & Metadata Deployment 16.2 Unlocked Packages, Metadata API & Salesforce DX 16.3 Release Readiness & Credential Maintenance

14.3 Test Data Strategies & Isolation

Key Takeaways Explain This Topic AI

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 isolation Symptom
  • Depends on a named Account in UAT Fails in scratch org
  • Assumes a specific User or Queue Id Fails after refresh
  • Counts “all Opportunities” Intermittent failures when parallel tests or other data exist
  • Uses production Id hard-codes Instant 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

Default isolation ( SeeAllData=false ).

@TestSetup for shared base records in the class.

Factories for repeated graphs and required fields.

Test.loadData for wide CSV fixtures when useful.

runAs for user/sharing scenarios (section 14.2).

Avoid SeeAllData=true .

Exam Checklist

Tests do not see org data by default .

@TestSetup builds shared per-class data efficiently.

Test.loadData + static resource CSVs load bulk fixtures.

Factories keep required-field construction DRY.

SeeAllData=true is discouraged ; create Users/queues carefully and mind mixed DML.

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