---
title: "@TestSetup: what it saves and what it breaks"
url: "https://trailassembly.com/apex-testsetup/"
topic: "Apex and Development"
published: "2026-09-11T12:51:30+00:00"
updated: "2026-10-06T13:53:06+00:00"
---

# @TestSetup: what it saves and what it breaks

**The short answer:** `@TestSetup` runs once per test class and its records are rolled back to that state before each test method. It is the biggest single speed-up available in an Apex test class, and it breaks in three specific situations that are worth knowing before you convert everything to use it.

## What it saves

Without it, every test method builds its own data. Twenty methods each inserting fifty records is a thousand inserts and all the automation that comes with them. With it, the setup runs once and the platform restores a snapshot between methods.

```apex
@IsTest
private class OpportunityServiceTest {

    @TestSetup
    static void makeData() {
        Account a = new Account(Name = 'Test Co');
        insert a;
        insert new List<Opportunity>{
            new Opportunity(Name='A', AccountId=a.Id, StageName='Prospecting',
                            CloseDate=Date.today().addDays(30)),
            new Opportunity(Name='B', AccountId=a.Id, StageName='Closed Won',
                            CloseDate=Date.today())
        };
    }

    @IsTest
    static void countsOpenOnly() {
        Account a = [SELECT Id FROM Account LIMIT 1];
        System.assertEquals(1, OpportunityService.openCount(a.Id));
    }
}
```

Note the re-query. The setup method’s variables are not visible to the test methods, so each test queries for what it needs. That is a feature — it means a test cannot accidentally depend on an in-memory object that does not match what was saved.

## The three things that break it

**1. A test class with `@IsTest(SeeAllData=true)`.** `@TestSetup` is not supported there at all. If you have both, the setup silently does not run, which produces a class where every test fails for reasons that look unrelated to data.

**2. A failure inside the setup method.** It fails every test in the class with the same message, pointing at the setup rather than at the test. This is mostly good, and it means one bad required field in setup masks whatever the tests were actually checking.

**3. Records created in setup cannot be cleaned up selectively.** The rollback restores the snapshot. A test that deletes a record gets it back for the next method, which is usually what you want and occasionally not what you assumed.

## What should not go in it

**Anything specific to one test.** Setup data is shared, so a record that exists only for one method makes every other method’s queries less predictable. Build that inside the method.

**Users.** Inserting a User alongside other records is a mixed DML operation and throws. If tests need a user, create them in a `System.runAs` block or a separate utility, not in setup. The rule is in [MIXED_DML_OPERATION](https://trailassembly.com/mixed-dml-operation/).

**Anything that calls out.** Setup cannot make callouts, and `Test.setMock` registered in a test method does not apply retroactively.

## It does not make tests independent

This is the trap. The snapshot restores data, not statics, not the flex queue, and not anything a previous test method wrote to a custom setting held in a static. Tests that pass individually and fail as a class usually have static state, not data state, and the setup method is not the fix.

## Combine it with a factory, not instead of one

The setup method should call a factory rather than containing the field-by-field construction. When someone makes a field required next year, one factory changes and every test class keeps working. Setup methods full of literal `new Account(...)` calls are the reason a schema change turns into a two-day job across forty test classes.

```apex
@TestSetup
static void makeData() {
    Account a = TestFactory.account().build();
    insert a;
    insert TestFactory.opportunities(a.Id, 2);
}
```

## Frequently asked

**How many setup methods can a class have?** One. More than one is a compile error.

**Does it count against limits?** It gets its own transaction with its own limits, separate from each test method’s.

**Does the rollback cost time?** Less than re-running the setup. On a class with more than two or three methods it is faster, and the gap widens with every method you add.

**Can I query setup data in `@IsTest(isParallel=true)` classes?** Yes, and parallel classes are where the speed-up matters most, because the setup cost is paid once per class rather than once per method.

---

Source: https://trailassembly.com/apex-testsetup/
Trail Assembly — independent Salesforce write-ups.
