What Causes Flaky Salesforce Tests (and How to Fix Them)

By TestZeus

What Causes Flaky Salesforce Tests (and How to Fix Them)

By TestZeus

What Causes Flaky Salesforce Tests (and How to Fix Them)

By TestZeus

Dithered illustration of a test run window where the same commit passes, fails, then passes

A flaky Salesforce test passes on one run and fails on the next, with no change to the code, the config or the test. In most cases the test checked something before Salesforce had finished doing it. The rest come from shared test data, parallel runs, logins and outside systems. This post covers the eight usual causes, how to spot each one, and the fix.

Flaky is not the same as broken. A broken test fails every time, usually after a release or a page change. If that is your problem, read why Salesforce UI tests break instead.

Why a small flake rate hurts a big suite

Flakiness is common even at companies with large test teams. In 2016, Google reported that almost 16% of its tests showed some flakiness, and that about 84% of the pass-to-fail transitions it saw involved a flaky test.

The math is why it matters. If each test fails at random 0.5% of the time, the chance that a full run of 200 tests comes back green is 0.995^200, which is about 37%. At 500 tests it drops to about 8%.

Line chart of the chance that a whole Salesforce test suite passes as the number of tests grows, at flake rates of 0.1%, 0.5% and 1% per test. At 200 tests and 0.5%, about 37% of runs are fully green.

The chart assumes each test flakes on its own. Real flakes cluster, since one slow sandbox can fail ten tests at once. The point still holds. A red run stops meaning “something broke”, people rerun until it is green, and real bugs slip through. To put a number on those reruns, try the regression testing cost calculator.

How to tell a flaky test from a broken one

Rerun the failed test on the same commit, against the same org, without changing anything. If it passes, it is flaky. If it fails again in the same place, it is broken. Playwright uses the same rule: with retries turned on, a test that failed on the first run but passed on a retry is reported as flaky.

Keep a record of which tests needed a rerun. One rerun is a data point. The same test needing reruns twice in a week is a pattern.

1. The test acts before the page is ready

Lightning pages load in pieces. The record header, related lists and each component render on their own schedule, and spinners and toasts come and go. A test that clicks as soon as an element exists can hit a button that is still disabled, or a field that is about to be redrawn.

How to spot it: The failure is a timeout, “element not found”, or a click that did nothing. It fails more on slow sandboxes and on the first run of the day.

Fix: Wait for the state you need, not for a fixed time. Before a click, Playwright already waits until the element is visible, stable, enabled and not covered by something else. Add explicit waits for what it cannot know about, such as a spinner going away or a toast appearing. Remove fixed sleeps. They are too short on a slow day and waste time on a fast one.

2. Async work in the org has not finished

Saving a record often starts work that runs later: queueable and future Apex, platform events, and flow paths that run asynchronously. Salesforce’s docs say each queued job runs when system resources become available. Usually that is seconds. Sometimes it is longer.

Apex unit tests hide this, because async work started in a test runs synchronously after Test.stopTest(). A UI test has no stopTest. It saves the record, opens the page and checks a field that a background job has not set yet.

How to spot it: The failing check is on a value set by automation, not by the test. The value is correct when you look a minute later.

Fix: Poll for the result with a time limit. For example, query the record every few seconds for up to a minute. Checking the record with a SOQL query is faster and more reliable than reloading the page. Fail with a clear message when the time limit runs out, so a real failure still shows up.

3. Sharing changes take effect later

Tests that change an owner or a sharing rule and then check access in the next step depend on timing. Salesforce’s Winter ‘27 preview notes say Apex- and Flow-triggered sharing changes will stop updating share records immediately, with the change planned for Spring ‘27. We cover it in our Winter ‘27 testing post. Preview timing can move, so check the final notes against the Salesforce release calendar.

How to spot it: A test that checks “user B can now see the record” passes alone and fails in a full run, or the other way round.

Fix: Treat access as eventually consistent. Poll for the access you expect with a time limit, the same way as cause 2. Logging in as the second user to check is still the right test. Our post on testing as more than one user covers that part.

4. Tests share data or depend on order

A test that uses an existing Account, or a record created by the test before it, depends on things it does not control. Another test renames the record, deletes it, or adds a second one with the same name, and the search returns the wrong row.

How to spot it: The test passes alone and fails in the full suite, or fails only when the run order changes. Errors mention duplicate values, missing records, or a list with more rows than expected.

Fix: Each test creates the records it needs, with unique names (add a run ID or a timestamp), and cleans up after. No test should rely on another test running first. Salesforce gives the same advice for Apex tests: create the data in the test, so tests don’t have to rely on data in a particular org.

5. Parallel runs lock the same records

Running tests in parallel saves time but adds contention. Salesforce documents UNABLE_TO_LOCK_ROW errors when tests update the same records at the same time, and deadlocks when two tests wait on each other. The same thing happens with UI tests when two workers edit the same record or its related records.

How to spot it: UNABLE_TO_LOCK_ROW in the error or the debug log, or failures that appear only when you add more parallel workers.

Fix: Give each parallel worker its own records, which the fix for cause 4 already does. For Apex test runs, Salesforce lets you turn on Disable Parallel Apex Testing. Treat that as a stopgap, since it makes the whole run slower.

6. Login and session problems

CI runners often start a fresh browser on every run. Salesforce challenges users to verify their identity when they log in from an unrecognized browser or device. A run that lands on a verification screen fails at step one. Long suites can also outlast the session timeout.

How to spot it: Failures cluster at login, or late in long runs. Screenshots show a verification page, or the login page in the middle of a test.

Fix: Log in once per run and reuse the saved session in each test. Use a dedicated test user for each profile you test. Agree with your admin how CI runners should log in, for example trusted IP ranges for the runners, rather than turning verification off. Check the session timeout against your longest run.

7. The sandbox or an integration is slow or down

A shared sandbox has other people in it. Data loads, deployments and other test runs all slow it down. Integrations add another system. A callout to a partner’s test environment that is slow or offline fails your test even though your org is fine.

How to spot it: Many unrelated tests fail in the same time window. Errors are timeouts or callout failures. The same run passes at night.

Fix: Know what else runs in the sandbox and when. Our test environment strategy post covers which sandbox to use for which tests. Keep tests that depend on outside systems in their own group, so an outage there does not turn the whole run red. Stub the outside system when the test is about your logic, not the integration.

8. Retries that hide the problem

Automatic retries are not a cause, but they make every cause above worse. A test that passes on the second try shows green, so nobody looks at it, and its flake rate grows until one retry is not enough.

How to spot it: Your runs are green but slow, and the report shows retried or flaky tests every day.

Fix: Keep retries if you need them, but report them. Count every test that needed a retry as flaky and put it on a list to fix.

Which one is it?

Symptom

Likely cause

First fix

Timeout or “element not found”, worse on slow days

1. Page not ready

Wait for the state, remove sleeps

A field checked right after save is empty, then correct a minute later

2. Async work

Poll the record with a time limit

An access check after a sharing change fails some of the time

3. Sharing changes

Poll for access

Passes alone, fails in the full suite

4. Shared data or order

Each test creates its own data

UNABLE_TO_LOCK_ROW, worse with more workers

5. Record locks

Separate records per worker

Verification page or login page mid-run

6. Login and session

Reuse a session, agree a CI login setup

Many unrelated tests fail at the same time

7. Sandbox or integration

Isolate integration tests

Green runs, but retries every day

8. Hidden retries

Report retried tests as flaky

How to manage flaky tests

You will not fix every flaky test at once. A simple process keeps them from piling up.

  • Measure. For each test, track how often it fails and then passes on a rerun of the same commit. That is its flake rate.

  • Quarantine. Move a test with a high flake rate out of the blocking run into a separate run, with an owner and a date to fix it. It still runs. It just stops blocking releases.

  • Fix in order. Start with the tests that flake most and run most often.

  • Set a bar. Agree on a flake rate a new test must stay under before it joins the blocking run.

  • Bring it back. When the fix holds for a week or two of runs, move the test back.

Where TestZeus fits

TestZeus runs Salesforce tests written in plain English or Gherkin. A test can check records with SOQL in the same flow, which is a direct way to confirm async work and sharing changes (causes 2 and 3). Tests can log in as different profiles. Test Blocks turn a working run into readable, step-by-step Playwright code.

It does not fix shared test data, parallel runs on the same records, or a crowded sandbox. Those come from how you set up your tests and environments, and the fixes above apply whatever tool you use.

FAQ

What is a flaky test in Salesforce?

A test that passes and fails on the same code and the same org. In Salesforce the usual causes are timing (page loads and async automation), shared data, parallel runs and logins.

Does Disable Parallel Apex Testing fix flaky tests?

It stops UNABLE_TO_LOCK_ROW errors caused by Apex tests running at the same time. It also makes runs slower and does nothing for UI tests. Separate test data for each test is the lasting fix.

Should I add retries to my Salesforce test runs?

Retries keep a pipeline moving, but only if every retried test is reported and tracked. Without that, they hide flaky tests until those tests fail every time.

How many flaky tests is too many?

There is no standard number. Track the share of runs that come back fully green without a rerun. If people rerun failed builds out of habit, the suite has too many.

// Start testing //

balance cost, quality and deadlines with TestZeus' Agents.

balance cost, quality and deadlines with TestZeus' Agents.